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.Web/NET45/Caching/DiskCache.cs b/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
index 7e30aaf9a..67445ee3a 100644
--- a/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
+++ b/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
@@ -306,6 +306,18 @@ namespace ImageProcessor.Web.Caching
return isUpdated;
}
+ ///
+ /// Gets the set to the last write time of the file.
+ ///
+ ///
+ /// The last write time of the file.
+ ///
+ internal async Task GetLastWriteTimeAsync()
+ {
+ // Create Action delegate for TrimCachedFolders.
+ return await TaskHelpers.Run(this.GetLastWriteTime);
+ }
+
///
/// Sets the LastWriteTime of the cached file to match the original file.
///
@@ -448,6 +460,26 @@ namespace ImageProcessor.Web.Caching
return cachedPath;
}
+ ///
+ /// Gets last modified time of the image.
+ ///
+ ///
+ /// The representing the last modified time of the image.
+ ///
+ private DateTime GetLastWriteTime()
+ {
+ string key = Path.GetFileNameWithoutExtension(this.CachedPath);
+ CachedImage cachedImage;
+ DateTime dateTime = DateTime.UtcNow;
+
+ if (PersistantDictionary.Instance.TryGetValue(key, out cachedImage))
+ {
+ dateTime = cachedImage.LastWriteTimeUtc;
+ }
+
+ return dateTime;
+ }
+
///
/// The rough date time compare.
///
diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
index 2cd0d3b1f..df1363b92 100644
--- a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
+++ b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
@@ -304,6 +304,23 @@ namespace ImageProcessor.Web.HttpModules
// Store the response type in the context for later retrieval.
context.Items[CachedResponseTypeKey] = ImageUtils.GetResponseType(fullPath).ToDescription();
+ string incomingEtag = context.Request.Headers["If-None-Match"];
+
+ if (incomingEtag != null && !isNewOrUpdated)
+ {
+ // Explicitly set the Content-Length header so the client doesn't wait for
+ // content but keeps the connection open for other requests
+ context.Response.AddHeader("Content-Length", "0");
+ context.Response.StatusCode = (int)HttpStatusCode.NotModified;
+ context.Response.SuppressContent = true;
+
+ this.SetHeaders(context, (string)context.Items[CachedResponseTypeKey]);
+
+ if (!isRemote)
+ {
+ return;
+ }
+ }
// The cached file is valid so just rewrite the path.
context.RewritePath(cache.GetVirtualCachedPath(), false);
@@ -334,7 +351,7 @@ namespace ImageProcessor.Web.HttpModules
response.AddHeader("Image-Served-By", "ImageProcessor.Web/" + AssemblyVersion);
HttpCachePolicy cache = response.Cache;
-
+ cache.SetCacheability(HttpCacheability.Public);
cache.VaryByHeaders["Accept-Encoding"] = true;
int maxDays = DiskCache.MaxFileCachedDuration;
@@ -342,19 +359,6 @@ namespace ImageProcessor.Web.HttpModules
cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(maxDays));
cache.SetMaxAge(new TimeSpan(maxDays, 0, 0, 0));
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
-
- string incomingEtag = context.Request.Headers["If-None-Match"];
-
- cache.SetCacheability(HttpCacheability.Public);
-
- if (incomingEtag == null)
- {
- return;
- }
-
- response.Clear();
- response.StatusCode = (int)HttpStatusCode.NotModified;
- response.SuppressContent = true;
}
#endregion
}
diff --git a/src/ImageProcessor.Web/NET45/Properties/AssemblyInfo.cs b/src/ImageProcessor.Web/NET45/Properties/AssemblyInfo.cs
index e6083400a..d2b87b19c 100644
--- a/src/ImageProcessor.Web/NET45/Properties/AssemblyInfo.cs
+++ b/src/ImageProcessor.Web/NET45/Properties/AssemblyInfo.cs
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("2.3.0.4")]
-[assembly: AssemblyFileVersion("2.3.0.4")]
+[assembly: AssemblyVersion("2.3.0.6")]
+[assembly: AssemblyFileVersion("2.3.0.6")]
diff --git a/src/ImageProcessor/Helpers/Extensions/StringExtensions.cs b/src/ImageProcessor/Helpers/Extensions/StringExtensions.cs
index 6b3c1a5be..723c679b4 100644
--- a/src/ImageProcessor/Helpers/Extensions/StringExtensions.cs
+++ b/src/ImageProcessor/Helpers/Extensions/StringExtensions.cs
@@ -1,9 +1,12 @@
-// -----------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
//
-// Copyright (c) James South.
-// Licensed under the Apache License, Version 2.0.
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
//
-// -----------------------------------------------------------------------
+//
+// Encapsulates a series of time saving extension methods to the class.
+//
+// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Helpers.Extensions
{
diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs
index 9b683491e..8072d5731 100644
--- a/src/ImageProcessor/ImageFactory.cs
+++ b/src/ImageProcessor/ImageFactory.cs
@@ -38,6 +38,11 @@ namespace ImageProcessor
///
private ImageFormat backupImageFormat;
+ ///
+ /// The original extension.
+ ///
+ private string originalExtension;
+
///
/// Whether the image is indexed.
///
@@ -184,6 +189,7 @@ namespace ImageProcessor
this.JpegQuality = DefaultJpegQuality;
ImageFormat imageFormat = ImageUtils.GetImageFormat(imageName);
this.backupImageFormat = imageFormat;
+ this.originalExtension = Path.GetExtension(this.ImagePath);
this.ImageFormat = imageFormat;
this.isIndexed = ImageUtils.IsIndexed(this.Image);
this.ShouldProcess = true;
@@ -315,6 +321,32 @@ namespace ImageProcessor
return this;
}
+ ///
+ /// Constrains the current image, resizing it to fit within the given dimensions whilst keeping its aspect ratio.
+ ///
+ ///
+ /// The containing the maximum width and height to set the image to.
+ ///
+ ///
+ /// The current instance of the class.
+ ///
+ public ImageFactory Constrain(Size size)
+ {
+ if (this.ShouldProcess)
+ {
+ int width = size.Width;
+ int height = size.Height;
+
+ var constrainSettings = new Dictionary { { "MaxWidth", width.ToString("G") }, { "MaxHeight", height.ToString("G") } };
+
+ Constrain constrain = new Constrain { DynamicParameter = new Size(width, height), Settings = constrainSettings };
+
+ this.Image = constrain.ProcessImage(this);
+ }
+
+ return this;
+ }
+
///
/// Changes the contrast of the current image.
///
@@ -343,6 +375,8 @@ namespace ImageProcessor
return this;
}
+
+
///
/// Crops the current image to the given location and size.
///
@@ -612,7 +646,7 @@ namespace ImageProcessor
// We need to check here if the path has an extension and remove it if so.
// This is so we can add the correct image format.
int length = filePath.LastIndexOf(".", StringComparison.Ordinal);
- string extension = ImageUtils.GetExtensionFromImageFormat(this.ImageFormat);
+ string extension = ImageUtils.GetExtensionFromImageFormat(this.ImageFormat, this.originalExtension);
filePath = length == -1 ? filePath + extension : filePath.Substring(0, length) + extension;
// Fix the colour palette of indexed images.
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/Imaging/ImageUtils.cs b/src/ImageProcessor/Imaging/ImageUtils.cs
index f8e69233c..58a9568ab 100644
--- a/src/ImageProcessor/Imaging/ImageUtils.cs
+++ b/src/ImageProcessor/Imaging/ImageUtils.cs
@@ -102,10 +102,13 @@ namespace ImageProcessor.Imaging
///
/// The to return the extension for.
///
+ ///
+ /// The original Extension.
+ ///
///
/// The correct file extension for the given .
///
- public static string GetExtensionFromImageFormat(ImageFormat imageFormat)
+ public static string GetExtensionFromImageFormat(ImageFormat imageFormat, string originalExtension)
{
switch (imageFormat.ToString())
{
@@ -117,8 +120,18 @@ namespace ImageProcessor.Imaging
return ".png";
case "Tif":
case "Tiff":
+ if (!string.IsNullOrWhiteSpace(originalExtension) && originalExtension.ToUpperInvariant() == ".TIFF")
+ {
+ return ".tiff";
+ }
+
return ".tif";
default:
+ if (!string.IsNullOrWhiteSpace(originalExtension) && originalExtension.ToUpperInvariant() == ".JPEG")
+ {
+ return ".jpeg";
+ }
+
return ".jpg";
}
}
diff --git a/src/ImageProcessor/Processors/Constrain.cs b/src/ImageProcessor/Processors/Constrain.cs
new file mode 100644
index 000000000..e2aaafaea
--- /dev/null
+++ b/src/ImageProcessor/Processors/Constrain.cs
@@ -0,0 +1,136 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Constrains an image to the given dimensions.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Processors
+{
+ #region Using
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.Text.RegularExpressions;
+ using ImageProcessor.Helpers.Extensions;
+ #endregion
+
+ ///
+ /// Constrains an image to the given dimensions.
+ ///
+ public class Constrain : ResizeBase
+ {
+ ///
+ /// The regular expression to search strings for.
+ ///
+ private static readonly Regex QueryRegex = new Regex(@"constrain=\d+[,-]\d+", RegexOptions.Compiled);
+
+ #region IGraphicsProcessor Members
+ ///
+ /// Gets the regular expression to search strings for.
+ ///
+ public override Regex RegexPattern
+ {
+ get
+ {
+ return QueryRegex;
+ }
+ }
+
+ ///
+ /// Gets or sets DynamicParameter.
+ ///
+ public override dynamic DynamicParameter { get; set; }
+
+ ///
+ /// Gets the order in which this processor is to be used in a chain.
+ ///
+ public override int SortOrder { get; protected set; }
+
+ ///
+ /// Gets or sets any additional settings required by the processor.
+ ///
+ public override Dictionary Settings { get; 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 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;
+ }
+
+ ///
+ /// Processes the image.
+ ///
+ ///
+ /// The the current instance of the class containing
+ /// the image to process.
+ ///
+ ///
+ /// The processed image from the current instance of the class.
+ ///
+ public override Image ProcessImage(ImageFactory factory)
+ {
+ double constrainedWidth = this.DynamicParameter.Width;
+ double constrainedHeight = this.DynamicParameter.Height;
+
+ Image 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 this.ResizeImage(factory, newSize.Width, newSize.Height, defaultMaxWidth, defaultMaxHeight);
+ }
+
+ return factory.Image;
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Processors/Crop.cs b/src/ImageProcessor/Processors/Crop.cs
index 0c2989a34..9023916b6 100644
--- a/src/ImageProcessor/Processors/Crop.cs
+++ b/src/ImageProcessor/Processors/Crop.cs
@@ -25,7 +25,7 @@ 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+", RegexOptions.Compiled);
#region IGraphicsProcessor Members
///
diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs
index 43cc489a4..dccaeb583 100644
--- a/src/ImageProcessor/Processors/Resize.cs
+++ b/src/ImageProcessor/Processors/Resize.cs
@@ -1,18 +1,18 @@
-// -----------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
//
-// Copyright (c) James South.
-// Licensed under the Apache License, Version 2.0.
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
//
-// -----------------------------------------------------------------------
+//
+// Resizes an image to the given dimensions.
+//
+// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Processors
{
#region Using
- using System;
using System.Collections.Generic;
using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
#endregion
@@ -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;
@@ -92,7 +92,7 @@ namespace ImageProcessor.Processors
// Set the index on the first instance only.
this.SortOrder = match.Index;
}
-
+
// Match syntax
if (match.Value.Contains("width"))
{
@@ -102,7 +102,7 @@ namespace ImageProcessor.Processors
{
size.Height = match.Value.ToPositiveIntegerArray()[0];
}
-
+
index += 1;
}
}
@@ -121,96 +121,18 @@ 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;
+ int width = this.DynamicParameter.Width ?? 0;
+ int height = this.DynamicParameter.Height ?? 0;
- 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();
- }
- }
+ int defaultMaxWidth;
+ int defaultMaxHeight;
+ int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth);
+ int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight);
- return image;
+ return this.ResizeImage(factory, width, height, defaultMaxWidth, defaultMaxHeight);
}
-
#endregion
}
}
diff --git a/src/ImageProcessor/Processors/ResizeBase.cs b/src/ImageProcessor/Processors/ResizeBase.cs
new file mode 100644
index 000000000..fce23eca5
--- /dev/null
+++ b/src/ImageProcessor/Processors/ResizeBase.cs
@@ -0,0 +1,179 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The resize base for inheriting resizable methods from.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Processors
+{
+ #region Using
+ using System;
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.Drawing.Drawing2D;
+ using System.Drawing.Imaging;
+ using System.Text.RegularExpressions;
+ #endregion
+
+ ///
+ /// The resize base for inheriting resizable methods from.
+ ///
+ public abstract class ResizeBase : IGraphicsProcessor
+ {
+ #region IGraphicsProcessor Members
+ ///
+ /// Gets the regular expression to search strings for.
+ ///
+ public abstract Regex RegexPattern { get; }
+
+ ///
+ /// Gets or sets DynamicParameter.
+ ///
+ public abstract dynamic DynamicParameter { get; set; }
+
+ ///
+ /// Gets or sets the order in which this processor is to be used in a chain.
+ ///
+ public abstract int SortOrder { get; protected set; }
+
+ ///
+ /// Gets or sets any additional settings required by the processor.
+ ///
+ public abstract Dictionary Settings { get; 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 abstract int MatchRegexIndex(string queryString);
+
+ ///
+ /// Processes the image.
+ ///
+ ///
+ /// The the current instance of the class containing
+ /// the image to process.
+ ///
+ ///
+ /// The processed image from the current instance of the class.
+ ///
+ public abstract Image ProcessImage(ImageFactory factory);
+
+ ///
+ /// The resize image.
+ ///
+ ///
+ /// The the current instance of the class containing
+ /// the image to process.
+ ///
+ ///
+ /// The width to resize the image to.
+ ///
+ ///
+ /// The height to resize the image to.
+ ///
+ ///
+ /// The default max width to resize the image to.
+ ///
+ ///
+ /// The default max height to resize the image to.
+ ///
+ ///
+ /// The processed image from the current instance of the class.
+ ///
+ 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;
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Properties/AssemblyInfo.cs b/src/ImageProcessor/Properties/AssemblyInfo.cs
index 029d9838c..c5c28955b 100644
--- a/src/ImageProcessor/Properties/AssemblyInfo.cs
+++ b/src/ImageProcessor/Properties/AssemblyInfo.cs
@@ -32,6 +32,6 @@ using System.Security;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("1.7.0.3")]
-[assembly: AssemblyFileVersion("1.7.0.3")]
+[assembly: AssemblyVersion("1.7.1.1")]
+[assembly: AssemblyFileVersion("1.7.1.1")]
diff --git a/src/Nuget/ImageProcessor.1.7.0.0.nupkg b/src/Nuget/ImageProcessor.1.7.0.0.nupkg
deleted file mode 100644
index 1d3cdec5d..000000000
Binary files a/src/Nuget/ImageProcessor.1.7.0.0.nupkg and /dev/null differ
diff --git a/src/Nuget/ImageProcessor.1.7.0.1.nupkg b/src/Nuget/ImageProcessor.1.7.0.1.nupkg
deleted file mode 100644
index 3d5bdf876..000000000
Binary files a/src/Nuget/ImageProcessor.1.7.0.1.nupkg and /dev/null differ
diff --git a/src/Nuget/ImageProcessor.1.7.0.2.nupkg b/src/Nuget/ImageProcessor.1.7.0.2.nupkg
deleted file mode 100644
index 7d0ffca1c..000000000
Binary files a/src/Nuget/ImageProcessor.1.7.0.2.nupkg and /dev/null differ
diff --git a/src/Nuget/ImageProcessor.1.7.0.3.nupkg b/src/Nuget/ImageProcessor.1.7.0.3.nupkg
deleted file mode 100644
index af5814e1e..000000000
Binary files a/src/Nuget/ImageProcessor.1.7.0.3.nupkg and /dev/null differ
diff --git a/src/Nuget/ImageProcessor.1.7.1.1.nupkg b/src/Nuget/ImageProcessor.1.7.1.1.nupkg
new file mode 100644
index 000000000..6b8c4ea7c
Binary files /dev/null and b/src/Nuget/ImageProcessor.1.7.1.1.nupkg differ
diff --git a/src/Nuget/ImageProcessor.Web.2.3.0.3.nupkg.REMOVED.git-id b/src/Nuget/ImageProcessor.Web.2.3.0.3.nupkg.REMOVED.git-id
deleted file mode 100644
index a026a9cf9..000000000
--- a/src/Nuget/ImageProcessor.Web.2.3.0.3.nupkg.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-0a367af8c6588fe2be54ef5950820a7f06f7b0f1
\ No newline at end of file
diff --git a/src/Nuget/ImageProcessor.Web.2.3.0.4.nupkg.REMOVED.git-id b/src/Nuget/ImageProcessor.Web.2.3.0.4.nupkg.REMOVED.git-id
deleted file mode 100644
index ea6008fcb..000000000
--- a/src/Nuget/ImageProcessor.Web.2.3.0.4.nupkg.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-129834a775acc5e2bfe85dc7db9edd37b9219e43
\ No newline at end of file
diff --git a/src/Nuget/ImageProcessor.Web.2.3.0.5.nupkg.REMOVED.git-id b/src/Nuget/ImageProcessor.Web.2.3.0.5.nupkg.REMOVED.git-id
deleted file mode 100644
index 7b70614ce..000000000
--- a/src/Nuget/ImageProcessor.Web.2.3.0.5.nupkg.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-9d6097d930bbf60f2d333f80e7c916759a3c3f0c
\ No newline at end of file
diff --git a/src/Nuget/ImageProcessor.Web.2.3.0.6.nupkg.REMOVED.git-id b/src/Nuget/ImageProcessor.Web.2.3.0.6.nupkg.REMOVED.git-id
new file mode 100644
index 000000000..a7d54c05f
--- /dev/null
+++ b/src/Nuget/ImageProcessor.Web.2.3.0.6.nupkg.REMOVED.git-id
@@ -0,0 +1 @@
+7f1cb06ddbb5a91892188c8a18109d8db0f76115
\ No newline at end of file
diff --git a/src/TestWebsites/NET4/Web.config b/src/TestWebsites/NET4/Web.config
index 134e1a427..1335fd3b2 100644
--- a/src/TestWebsites/NET4/Web.config
+++ b/src/TestWebsites/NET4/Web.config
@@ -83,6 +83,12 @@
+
+
+
+
+
+
diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj b/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj
index 35a89bd88..b5a9065fa 100644
--- a/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj
+++ b/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj
@@ -165,7 +165,9 @@
-
+
+ Designer
+
diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Web.config b/src/TestWebsites/NET45/Test_Website_NET45/Web.config
index fb3bbb368..c2a70cb67 100644
--- a/src/TestWebsites/NET45/Test_Website_NET45/Web.config
+++ b/src/TestWebsites/NET45/Test_Website_NET45/Web.config
@@ -5,80 +5,86 @@
-->
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+