diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs
index 8072d5731..429e4ff44 100644
--- a/src/ImageProcessor/ImageFactory.cs
+++ b/src/ImageProcessor/ImageFactory.cs
@@ -375,8 +375,6 @@ namespace ImageProcessor
return this;
}
-
-
///
/// Crops the current image to the given location and size.
///
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index 1efd252d1..ea024b3a6 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -62,6 +62,7 @@
+
@@ -75,6 +76,7 @@
+
diff --git a/src/ImageProcessor/Imaging/ResizeLayer.cs b/src/ImageProcessor/Imaging/ResizeLayer.cs
new file mode 100644
index 000000000..026990fe9
--- /dev/null
+++ b/src/ImageProcessor/Imaging/ResizeLayer.cs
@@ -0,0 +1,115 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Encapsulates the properties required to resize an image.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging
+{
+ #region Using
+ using System.Drawing;
+ #endregion
+
+ ///
+ /// Encapsulates the properties required to resize an image.
+ ///
+ public class ResizeLayer
+ {
+ #region Constructors
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ResizeLayer()
+ {
+ this.ResizeMode = ResizeMode.Pad;
+ this.BackgroundColor = Color.Transparent;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The resize mode to apply to resized image.
+ ///
+ public ResizeLayer(ResizeMode resizeMode)
+ {
+ this.ResizeMode = resizeMode;
+ this.BackgroundColor = Color.Transparent;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The resize mode to apply to resized image.
+ ///
+ ///
+ /// The to set as the background color.
+ /// Used for image formats that do not support transparency
+ ///
+ public ResizeLayer(ResizeMode resizeMode, Color backgroundColor)
+ {
+ this.ResizeMode = resizeMode;
+ this.BackgroundColor = backgroundColor;
+ }
+ #endregion
+
+ #region Properties
+ ///
+ /// Gets or sets the size.
+ ///
+ public Size Size { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to ResizeMode the layer.
+ ///
+ public ResizeMode ResizeMode { get; set; }
+
+ ///
+ /// Gets or sets the background color.
+ ///
+ public Color BackgroundColor { get; set; }
+ #endregion
+
+ ///
+ /// Returns a value that indicates whether the specified object is an
+ /// object that is equivalent to
+ /// this object.
+ ///
+ ///
+ /// The object to test.
+ ///
+ ///
+ /// True if the given object is an object that is equivalent to
+ /// this object; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ ResizeLayer resizeLayer = obj as ResizeLayer;
+
+ if (resizeLayer == null)
+ {
+ return false;
+ }
+
+ return this.Size == resizeLayer.Size
+ && this.ResizeMode == resizeLayer.ResizeMode
+ && this.BackgroundColor == resizeLayer.BackgroundColor;
+ }
+
+ ///
+ /// Returns a hash code value that represents this object.
+ ///
+ ///
+ /// A hash code that represents this object.
+ ///
+ public override int GetHashCode()
+ {
+ return this.Size.GetHashCode() + this.ResizeMode.GetHashCode() + this.BackgroundColor.GetHashCode();
+ }
+ }
+}
diff --git a/src/ImageProcessor/Imaging/ResizeMode.cs b/src/ImageProcessor/Imaging/ResizeMode.cs
new file mode 100644
index 000000000..144a5a485
--- /dev/null
+++ b/src/ImageProcessor/Imaging/ResizeMode.cs
@@ -0,0 +1,28 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Enumerated resize modes to apply to resized images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging
+{
+ ///
+ /// Enumerated resize modes to apply to resized images.
+ ///
+ public enum ResizeMode
+ {
+ ///
+ /// Pads the resized image to fit the bounds of its container.
+ ///
+ Pad,
+
+ ///
+ /// Stretches the resized image to fit the bounds of its container.
+ ///
+ Stretch
+ }
+}
diff --git a/src/ImageProcessor/Processors/Constrain.cs b/src/ImageProcessor/Processors/Constrain.cs
index e2aaafaea..51528b05d 100644
--- a/src/ImageProcessor/Processors/Constrain.cs
+++ b/src/ImageProcessor/Processors/Constrain.cs
@@ -15,6 +15,8 @@ namespace ImageProcessor.Processors
using System.Drawing;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
+ using ImageProcessor.Imaging;
+
#endregion
///
@@ -126,7 +128,7 @@ namespace ImageProcessor.Processors
int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth);
int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight);
- return this.ResizeImage(factory, newSize.Width, newSize.Height, defaultMaxWidth, defaultMaxHeight);
+ return this.ResizeImage(factory, newSize.Width, newSize.Height, defaultMaxWidth, defaultMaxHeight, ResizeMode.Pad, Color.Transparent);
}
return factory.Image;
diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs
index dccaeb583..1b6a73104 100644
--- a/src/ImageProcessor/Processors/Resize.cs
+++ b/src/ImageProcessor/Processors/Resize.cs
@@ -11,10 +11,15 @@
namespace ImageProcessor.Processors
{
#region Using
+
+ using System;
using System.Collections.Generic;
using System.Drawing;
+ using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
+ using ImageProcessor.Imaging;
+
#endregion
///
@@ -25,7 +30,22 @@ namespace ImageProcessor.Processors
///
/// The regular expression to search strings for.
///
- private static readonly Regex QueryRegex = new Regex(@"(width|height)=\d+", RegexOptions.Compiled);
+ private static readonly Regex QueryRegex = new Regex(@"((width|height)=\d+)|(mode=(pad|stretch|crop))|(bgcolor-([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled);
+
+ ///
+ /// The regular expression to search strings for the size attribute.
+ ///
+ private static readonly Regex SizeRegex = new Regex(@"(width|height)=\d+");
+
+ ///
+ /// The regular expression to search strings for the mode attribute.
+ ///
+ private static readonly Regex ModeRegex = new Regex(@"mode=(pad|stretch|crop)");
+
+ ///
+ /// The regular expression to search strings for the color attribute.
+ ///
+ private static readonly Regex ColorRegex = new Regex(@"bgcolor-([0-9a-fA-F]{3}){1,2}", RegexOptions.Compiled);
#region IGraphicsProcessor Members
///
@@ -81,7 +101,10 @@ namespace ImageProcessor.Processors
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
- Size size = new Size();
+ ResizeLayer resizeLayer = new ResizeLayer();
+
+ // First merge the matches so we can parse .
+ StringBuilder stringBuilder = new StringBuilder();
foreach (Match match in this.RegexPattern.Matches(queryString))
{
@@ -93,21 +116,20 @@ namespace ImageProcessor.Processors
this.SortOrder = match.Index;
}
- // Match syntax
- if (match.Value.Contains("width"))
- {
- size.Width = match.Value.ToPositiveIntegerArray()[0];
- }
- else
- {
- size.Height = match.Value.ToPositiveIntegerArray()[0];
- }
+ stringBuilder.Append(match.Value);
index += 1;
}
}
- this.DynamicParameter = size;
+ // Match syntax
+ string toParse = stringBuilder.ToString();
+
+ resizeLayer.Size = this.ParseSize(toParse);
+ resizeLayer.ResizeMode = this.ParseMode(toParse);
+ resizeLayer.BackgroundColor = this.ParseColor(toParse);
+
+ this.DynamicParameter = resizeLayer;
return this.SortOrder;
}
@@ -123,16 +145,115 @@ namespace ImageProcessor.Processors
///
public override Image ProcessImage(ImageFactory factory)
{
- int width = this.DynamicParameter.Width ?? 0;
- int height = this.DynamicParameter.Height ?? 0;
+ int width = this.DynamicParameter.Size.Width ?? 0;
+ int height = this.DynamicParameter.Size.Height ?? 0;
+ ResizeMode mode = this.DynamicParameter.ResizeMode;
+ Color backgroundColor = this.DynamicParameter.BackgroundColor;
int defaultMaxWidth;
int defaultMaxHeight;
int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth);
int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight);
- return this.ResizeImage(factory, width, height, defaultMaxWidth, defaultMaxHeight);
+ return this.ResizeImage(factory, width, height, defaultMaxWidth, defaultMaxHeight, mode, backgroundColor);
}
#endregion
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The .
+ ///
+ private Size ParseSize(string input)
+ {
+ const string Width = "width";
+ const string Height = "height";
+ Size size = new Size();
+
+ // First merge the matches so we can parse .
+ StringBuilder stringBuilder = new StringBuilder();
+ foreach (Match match in SizeRegex.Matches(input))
+ {
+ stringBuilder.Append(match.Value);
+ }
+
+ // First cater for single dimensions.
+ string value = stringBuilder.ToString();
+
+ if (input.Contains(Width) && !input.Contains(Height))
+ {
+ size = new Size(value.ToPositiveIntegerArray()[0], 0);
+ }
+
+ if (input.Contains(Height) && !input.Contains(Width))
+ {
+ size = new Size(0, value.ToPositiveIntegerArray()[0]);
+ }
+
+ // Both dimensions supplied.
+ if (input.Contains(Height) && input.Contains(Width))
+ {
+ int[] dimensions = value.ToPositiveIntegerArray();
+
+ // Check the order in which they have been supplied.
+ size = input.IndexOf(Width, StringComparison.Ordinal) < input.IndexOf(Height, StringComparison.Ordinal)
+ ? new Size(dimensions[0], dimensions[1])
+ : new Size(dimensions[1], dimensions[0]);
+ }
+
+ return size;
+ }
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct .
+ ///
+ private ResizeMode ParseMode(string input)
+ {
+ foreach (Match match in ModeRegex.Matches(input))
+ {
+ // Split on =
+ string mode = match.Value.Split('=')[1];
+
+ switch (mode)
+ {
+ case "stretch":
+ return ResizeMode.Stretch;
+ default:
+ return ResizeMode.Pad;
+ }
+ }
+
+ return ResizeMode.Pad;
+ }
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct
+ ///
+ private Color ParseColor(string input)
+ {
+ foreach (Match match in ColorRegex.Matches(input))
+ {
+ // split on color-hex
+ return ColorTranslator.FromHtml("#" + match.Value.Split('-')[1]);
+ }
+
+ return Color.Transparent;
+ }
}
}
diff --git a/src/ImageProcessor/Processors/ResizeBase.cs b/src/ImageProcessor/Processors/ResizeBase.cs
index fce23eca5..e0405d070 100644
--- a/src/ImageProcessor/Processors/ResizeBase.cs
+++ b/src/ImageProcessor/Processors/ResizeBase.cs
@@ -17,6 +17,9 @@ namespace ImageProcessor.Processors
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
+
+ using ImageProcessor.Imaging;
+
#endregion
///
@@ -87,10 +90,16 @@ namespace ImageProcessor.Processors
///
/// The default max height to resize the image to.
///
+ ///
+ /// Whether to pad the image to fill the set size.
+ ///
+ ///
+ /// The background color to pad the image with.
+ ///
///
/// The processed image from the current instance of the class.
///
- protected Image ResizeImage(ImageFactory factory, int width, int height, int defaultMaxWidth, int defaultMaxHeight)
+ protected Image ResizeImage(ImageFactory factory, int width, int height, int defaultMaxWidth, int defaultMaxHeight, ResizeMode resizeMode, Color backgroundColor)
{
Bitmap newImage = null;
Image image = factory.Image;
@@ -100,26 +109,56 @@ namespace ImageProcessor.Processors
int sourceWidth = image.Width;
int sourceHeight = image.Height;
+ int destinationWidth = width;
+ int destinationHeight = height;
+
int maxWidth = defaultMaxWidth > 0 ? defaultMaxWidth : int.MaxValue;
int maxHeight = defaultMaxHeight > 0 ? defaultMaxHeight : int.MaxValue;
+ // Fractional variants for preserving aspect ratio.
+ double percentHeight = Math.Abs(height / (double)sourceHeight);
+ double percentWidth = Math.Abs(width / (double)sourceWidth);
+
+ int destinationX = 0;
+ int destinationY = 0;
+
+ // Change the destination rectangle coordinates if padding and
+ // there has been a set width and height.
+ if (resizeMode == ResizeMode.Pad && width > 0 && height > 0)
+ {
+ double ratio;
+
+ if (percentHeight < percentWidth)
+ {
+ ratio = percentHeight;
+ destinationX = (int)((width - (sourceWidth * ratio)) / 2);
+ destinationWidth = (int)Math.Floor(sourceWidth * percentHeight);
+ }
+ else
+ {
+ ratio = percentWidth;
+ destinationY = (int)((height - (sourceHeight * ratio)) / 2);
+ destinationHeight = (int)Math.Floor(sourceHeight * percentWidth);
+ }
+ }
+
// 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);
+ destinationHeight = (int)Math.Floor(sourceHeight * percentWidth);
+ height = destinationHeight;
}
if (width == 0)
{
- float percentHeight = Math.Abs(height / (float)sourceHeight);
- width = (int)Math.Floor(sourceWidth * percentHeight);
+ destinationWidth = (int)Math.Floor(sourceWidth * percentHeight);
+ width = destinationWidth;
}
if (width > 0 && height > 0 && width <= maxWidth && height <= maxHeight)
{
- // Dont use an object initializer here.
+ // Don't use an object initializer here.
+ // ReSharper disable once UseObjectOrCollectionInitializer
newImage = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
newImage.Tag = image.Tag;
@@ -128,7 +167,7 @@ namespace ImageProcessor.Processors
// 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)
+ if (image.Width < destinationWidth && image.Height < destinationHeight)
{
// We are making it larger.
graphics.SmoothingMode = SmoothingMode.AntiAlias;
@@ -140,11 +179,9 @@ namespace ImageProcessor.Processors
{
// 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;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
}
// An unwanted border appears when using InterpolationMode.HighQualityBicubic to resize the image
@@ -154,7 +191,8 @@ namespace ImageProcessor.Processors
using (ImageAttributes wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
- Rectangle destRect = new Rectangle(0, 0, width, height);
+ graphics.Clear(backgroundColor);
+ Rectangle destRect = new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight);
graphics.DrawImage(image, destRect, 0, 0, sourceWidth, sourceHeight, GraphicsUnit.Pixel, wrapMode);
}
diff --git a/src/ImageProcessor/Processors/Rotate.cs b/src/ImageProcessor/Processors/Rotate.cs
index 831250700..ec7094393 100644
--- a/src/ImageProcessor/Processors/Rotate.cs
+++ b/src/ImageProcessor/Processors/Rotate.cs
@@ -24,7 +24,7 @@ namespace ImageProcessor.Processors
///
/// The regular expression to search strings for.
///
- private static readonly Regex QueryRegex = new Regex(@"rotate=((?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)|angle-(?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)\|bgcolor-([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled);
+ private static readonly Regex QueryRegex = new Regex(@"rotate=((?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)|angle-(?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)[\|,]bgcolor-([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled);
///
/// The regular expression to search strings for the angle attribute.
diff --git a/src/ImageProcessor/Settings.StyleCop b/src/ImageProcessor/Settings.StyleCop
index 253825567..0b178ac1e 100644
--- a/src/ImageProcessor/Settings.StyleCop
+++ b/src/ImageProcessor/Settings.StyleCop
@@ -1,4 +1,9 @@
+
+
+ behaviour
+
+
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 1f6ef52b7..0ac209e64 100644
--- a/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml
+++ b/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml
@@ -16,6 +16,20 @@
+
+
+
+
+
Padded
+

+ @*
Foreign language test.
+

*@
+
+
+ @*
Cropped
+

*@
+
+