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 @@ --> - - -
-
-
- - + + +
+
+
+ + - - - - - - - + + + + + + + - + - + - + - - - - - - - - - - + + + + + + + + + + - - - + + + - + - - + + - - - - - - - - + + + + + + + + - - - + + + - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + +