From faf6fdc6bf818645fd8c62c2df9325130969f793 Mon Sep 17 00:00:00 2001 From: James South Date: Mon, 2 Dec 2013 15:53:22 +0000 Subject: [PATCH] Adding new resize modes and config files Former-commit-id: 960fa630e2c2ccd72aff1ad4b96d576299e4f482 --- src/ImageProcessor.sln | 1 - src/ImageProcessor/ImageFactory.cs | 27 ++++++- src/ImageProcessor/ImageProcessor.csproj | 1 + src/ImageProcessor/Imaging/AnchorPosition.cs | 43 +++++++++++ src/ImageProcessor/Imaging/ResizeLayer.cs | 61 ++++++++++++---- src/ImageProcessor/Imaging/ResizeMode.cs | 12 +++- src/ImageProcessor/Processors/Constrain.cs | 2 +- src/ImageProcessor/Processors/Resize.cs | 71 ++++++++++++++++--- src/ImageProcessor/Processors/ResizeBase.cs | 65 +++++++++++++++-- .../Test_Website_NET45.csproj | 9 +++ .../Views/Home/Index.cshtml | 13 +++- .../NET45/Test_Website_NET45/Web.config | 34 ++------- .../config/imageprocessor/cache.config | 3 + .../config/imageprocessor/processing.config | 18 +++++ .../config/imageprocessor/security.config | 8 +++ 15 files changed, 310 insertions(+), 58 deletions(-) create mode 100644 src/ImageProcessor/Imaging/AnchorPosition.cs create mode 100644 src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/cache.config create mode 100644 src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/processing.config create mode 100644 src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/security.config diff --git a/src/ImageProcessor.sln b/src/ImageProcessor.sln index 4a629426d5..8bba5be7ee 100644 --- a/src/ImageProcessor.sln +++ b/src/ImageProcessor.sln @@ -78,7 +78,6 @@ Global {23CE0FC0-9E59-4C93-A604-A4A98A6284D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {23CE0FC0-9E59-4C93-A604-A4A98A6284D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {23CE0FC0-9E59-4C93-A604-A4A98A6284D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {23CE0FC0-9E59-4C93-A604-A4A98A6284D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {23CE0FC0-9E59-4C93-A604-A4A98A6284D1}.Debug|x86.ActiveCfg = Debug|Any CPU {23CE0FC0-9E59-4C93-A604-A4A98A6284D1}.Release|Any CPU.ActiveCfg = Release|Any CPU {23CE0FC0-9E59-4C93-A604-A4A98A6284D1}.Release|Any CPU.Build.0 = Release|Any CPU diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs index 429e4ff445..5092cfab3c 100644 --- a/src/ImageProcessor/ImageFactory.cs +++ b/src/ImageProcessor/ImageFactory.cs @@ -500,7 +500,32 @@ namespace ImageProcessor var resizeSettings = new Dictionary { { "MaxWidth", width.ToString("G") }, { "MaxHeight", height.ToString("G") } }; - Resize resize = new Resize { DynamicParameter = new Size(width, height), Settings = resizeSettings }; + ResizeLayer resizeLayer = new ResizeLayer(new Size(width, height)); + + Resize resize = new Resize { DynamicParameter = resizeLayer, Settings = resizeSettings }; + + this.Image = resize.ProcessImage(this); + } + + return this; + } + + /// + /// Resizes the current image to the given dimensions. + /// + /// + /// The containing the properties required to resize the image. + /// + /// + /// The current instance of the class. + /// + public ImageFactory Resize(ResizeLayer resizeLayer) + { + if (this.ShouldProcess) + { + var resizeSettings = new Dictionary { { "MaxWidth", resizeLayer.Size.Width.ToString("G") }, { "MaxHeight", resizeLayer.Size.Height.ToString("G") } }; + + Resize resize = new Resize { DynamicParameter = resizeLayer, Settings = resizeSettings }; this.Image = resize.ProcessImage(this); } diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index ea024b3a6e..f8fc363344 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -61,6 +61,7 @@ + diff --git a/src/ImageProcessor/Imaging/AnchorPosition.cs b/src/ImageProcessor/Imaging/AnchorPosition.cs new file mode 100644 index 0000000000..cb0dd56ae2 --- /dev/null +++ b/src/ImageProcessor/Imaging/AnchorPosition.cs @@ -0,0 +1,43 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Enumerated anchor positions to apply to resized images. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Imaging +{ + /// + /// Enumerated anchor positions to apply to resized images. + /// + public enum AnchorPosition + { + /// + /// Anchors the position of the image to the top of it's bounding container. + /// + Top, + + /// + /// Anchors the position of the image to the center of it's bounding container. + /// + Center, + + /// + /// Anchors the position of the image to the bottom of it's bounding container. + /// + Bottom, + + /// + /// Anchors the position of the image to the left of it's bounding container. + /// + Left, + + /// + /// Anchors the position of the image to the right of it's bounding container. + /// + Right + } +} diff --git a/src/ImageProcessor/Imaging/ResizeLayer.cs b/src/ImageProcessor/Imaging/ResizeLayer.cs index 026990fe92..c35a4d3d3a 100644 --- a/src/ImageProcessor/Imaging/ResizeLayer.cs +++ b/src/ImageProcessor/Imaging/ResizeLayer.cs @@ -23,38 +23,69 @@ namespace ImageProcessor.Imaging /// /// Initializes a new instance of the class. /// - public ResizeLayer() + /// + /// The containing the width and height to set the image to. + /// + public ResizeLayer(Size size) { + this.Size = size; this.ResizeMode = ResizeMode.Pad; + this.AnchorPosition = AnchorPosition.Center; this.BackgroundColor = Color.Transparent; } /// /// Initializes a new instance of the class. /// + /// + /// The containing the width and height to set the image to. + /// /// - /// The resize mode to apply to resized image. + /// The to apply to resized image. /// - public ResizeLayer(ResizeMode resizeMode) + public ResizeLayer(Size size, ResizeMode resizeMode) { + this.Size = size; this.ResizeMode = resizeMode; + this.AnchorPosition = AnchorPosition.Center; this.BackgroundColor = Color.Transparent; } /// /// Initializes a new instance of the class. /// - /// - /// The resize mode to apply to resized image. + /// + /// The containing the width and height to set the image to. + /// + /// + /// The to apply to resized image. /// + public ResizeLayer(Size size, AnchorPosition anchorPosition) + { + this.Size = size; + this.AnchorPosition = anchorPosition; + this.ResizeMode = ResizeMode.Pad; + this.BackgroundColor = Color.Transparent; + } + + /// + /// Initializes a new instance of the class. + /// /// /// The to set as the background color. /// Used for image formats that do not support transparency /// - public ResizeLayer(ResizeMode resizeMode, Color backgroundColor) + /// + /// The resize mode to apply to resized image. + /// + /// + /// The to apply to resized image. + /// + public ResizeLayer(Color backgroundColor, ResizeMode resizeMode = ResizeMode.Pad, AnchorPosition anchorPosition = AnchorPosition.Center) { - this.ResizeMode = resizeMode; this.BackgroundColor = backgroundColor; + this.ResizeMode = resizeMode; + this.AnchorPosition = anchorPosition; } #endregion @@ -65,14 +96,19 @@ namespace ImageProcessor.Imaging public Size Size { get; set; } /// - /// Gets or sets a value indicating whether to ResizeMode the layer. + /// Gets or sets the background color. + /// + public Color BackgroundColor { get; set; } + + /// + /// Gets or sets the resize mode. /// public ResizeMode ResizeMode { get; set; } /// - /// Gets or sets the background color. + /// Gets or sets the anchor position. /// - public Color BackgroundColor { get; set; } + public AnchorPosition AnchorPosition { get; set; } #endregion /// @@ -96,8 +132,9 @@ namespace ImageProcessor.Imaging return false; } - return this.Size == resizeLayer.Size + return this.Size == resizeLayer.Size && this.ResizeMode == resizeLayer.ResizeMode + && this.AnchorPosition == resizeLayer.AnchorPosition && this.BackgroundColor == resizeLayer.BackgroundColor; } @@ -109,7 +146,7 @@ namespace ImageProcessor.Imaging /// public override int GetHashCode() { - return this.Size.GetHashCode() + this.ResizeMode.GetHashCode() + this.BackgroundColor.GetHashCode(); + return this.Size.GetHashCode() + this.ResizeMode.GetHashCode() + this.AnchorPosition.GetHashCode() + this.BackgroundColor.GetHashCode(); } } } diff --git a/src/ImageProcessor/Imaging/ResizeMode.cs b/src/ImageProcessor/Imaging/ResizeMode.cs index 144a5a4856..8e140da849 100644 --- a/src/ImageProcessor/Imaging/ResizeMode.cs +++ b/src/ImageProcessor/Imaging/ResizeMode.cs @@ -23,6 +23,16 @@ namespace ImageProcessor.Imaging /// /// Stretches the resized image to fit the bounds of its container. /// - Stretch + Stretch, + + /// + /// Crops the resized image to fit the bounds of its container. + /// + Crop, + + /// + /// Constrains the resized image to fit the bounds of its container. + /// + Constrain } } diff --git a/src/ImageProcessor/Processors/Constrain.cs b/src/ImageProcessor/Processors/Constrain.cs index 51528b05d5..61ec51c145 100644 --- a/src/ImageProcessor/Processors/Constrain.cs +++ b/src/ImageProcessor/Processors/Constrain.cs @@ -128,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, ResizeMode.Pad, Color.Transparent); + return this.ResizeImage(factory, newSize.Width, newSize.Height, defaultMaxWidth, defaultMaxHeight, Color.Transparent); } return factory.Image; diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs index 4a100a1392..6abff11a5c 100644 --- a/src/ImageProcessor/Processors/Resize.cs +++ b/src/ImageProcessor/Processors/Resize.cs @@ -30,17 +30,22 @@ namespace ImageProcessor.Processors /// /// The regular expression to search strings for. /// - private static readonly Regex QueryRegex = new Regex(@"((width|height)=\d+)|(mode=(pad|stretch|crop))|(bgcolor=([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled); + private static readonly Regex QueryRegex = new Regex(@"((width|height)=\d+)|(mode=(pad|stretch|crop|constrain))|(anchor=(top|bottom|left|right|center))|(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+"); + private static readonly Regex SizeRegex = new Regex(@"(width|height)=\d+", RegexOptions.Compiled); /// /// The regular expression to search strings for the mode attribute. /// - private static readonly Regex ModeRegex = new Regex(@"mode=(pad|stretch|crop)"); + private static readonly Regex ModeRegex = new Regex(@"mode=(pad|stretch|crop|constrain)", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the anchor attribute. + /// + private static readonly Regex AnchorRegex = new Regex(@"anchor=(top|bottom|left|right|center)", RegexOptions.Compiled); /// /// The regular expression to search strings for the color attribute. @@ -101,7 +106,6 @@ namespace ImageProcessor.Processors // Set the sort order to max to allow filtering. this.SortOrder = int.MaxValue; - ResizeLayer resizeLayer = new ResizeLayer(); // First merge the matches so we can parse . StringBuilder stringBuilder = new StringBuilder(); @@ -125,9 +129,13 @@ namespace ImageProcessor.Processors // Match syntax string toParse = stringBuilder.ToString(); - resizeLayer.Size = this.ParseSize(toParse); - resizeLayer.ResizeMode = this.ParseMode(toParse); - resizeLayer.BackgroundColor = this.ParseColor(toParse); + Size size = this.ParseSize(toParse); + ResizeLayer resizeLayer = new ResizeLayer(size) + { + ResizeMode = this.ParseMode(toParse), + AnchorPosition = this.ParsePosition(toParse), + BackgroundColor = this.ParseColor(toParse) + }; this.DynamicParameter = resizeLayer; return this.SortOrder; @@ -148,6 +156,7 @@ namespace ImageProcessor.Processors int width = this.DynamicParameter.Size.Width ?? 0; int height = this.DynamicParameter.Size.Height ?? 0; ResizeMode mode = this.DynamicParameter.ResizeMode; + AnchorPosition anchor = this.DynamicParameter.AnchorPosition; Color backgroundColor = this.DynamicParameter.BackgroundColor; int defaultMaxWidth; @@ -155,7 +164,15 @@ namespace ImageProcessor.Processors int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth); int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight); - return this.ResizeImage(factory, width, height, defaultMaxWidth, defaultMaxHeight, mode, backgroundColor); + if (mode == ResizeMode.Constrain) + { + // Just use the old constrain plugin to handle the resize. + var constrainSettings = new Dictionary { { "MaxWidth", defaultMaxWidth.ToString("G") }, { "MaxHeight", defaultMaxHeight.ToString("G") } }; + Constrain constrain = new Constrain { DynamicParameter = new Size(width, height), Settings = constrainSettings }; + return constrain.ProcessImage(factory); + } + + return this.ResizeImage(factory, width, height, defaultMaxWidth, defaultMaxHeight, backgroundColor, mode, anchor); } #endregion @@ -228,6 +245,10 @@ namespace ImageProcessor.Processors { case "stretch": return ResizeMode.Stretch; + case "crop": + return ResizeMode.Crop; + case "constrain": + return ResizeMode.Constrain; default: return ResizeMode.Pad; } @@ -236,6 +257,40 @@ namespace ImageProcessor.Processors return ResizeMode.Pad; } + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct . + /// + private AnchorPosition ParsePosition(string input) + { + foreach (Match match in AnchorRegex.Matches(input)) + { + // Split on = + string anchor = match.Value.Split('=')[1]; + + switch (anchor) + { + case "top": + return AnchorPosition.Top; + case "bottom": + return AnchorPosition.Bottom; + case "left": + return AnchorPosition.Left; + case "right": + return AnchorPosition.Right; + default: + return AnchorPosition.Center; + } + } + + return AnchorPosition.Center; + } + /// /// Returns the correct for the given string. /// diff --git a/src/ImageProcessor/Processors/ResizeBase.cs b/src/ImageProcessor/Processors/ResizeBase.cs index e0405d070a..6868cb4929 100644 --- a/src/ImageProcessor/Processors/ResizeBase.cs +++ b/src/ImageProcessor/Processors/ResizeBase.cs @@ -90,16 +90,27 @@ 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 mode with which to resize the image. + /// + /// + /// The anchor position to place the image at. + /// /// /// The processed image from the current instance of the class. /// - protected Image ResizeImage(ImageFactory factory, int width, int height, int defaultMaxWidth, int defaultMaxHeight, ResizeMode resizeMode, Color backgroundColor) + protected Image ResizeImage( + ImageFactory factory, + int width, + int height, + int defaultMaxWidth, + int defaultMaxHeight, + Color backgroundColor, + ResizeMode resizeMode = ResizeMode.Pad, + AnchorPosition anchorPosition = AnchorPosition.Center) { Bitmap newImage = null; Image image = factory.Image; @@ -142,6 +153,52 @@ namespace ImageProcessor.Processors } } + // Change the destination rectangle coordinates if cropping and + // there has been a set width and height. + if (resizeMode == ResizeMode.Crop && width > 0 && height > 0) + { + double ratio; + + if (percentHeight < percentWidth) + { + ratio = percentWidth; + + switch (anchorPosition) + { + case AnchorPosition.Top: + destinationY = 0; + break; + case AnchorPosition.Bottom: + destinationY = (int)(height - (sourceHeight * ratio)); + break; + default: + destinationY = (int)((height - (sourceHeight * ratio)) / 2); + break; + } + + destinationHeight = (int)Math.Floor(sourceHeight * percentWidth); + } + else + { + ratio = percentHeight; + + switch (anchorPosition) + { + case AnchorPosition.Left: + destinationX = 0; + break; + case AnchorPosition.Right: + destinationX = (int)(width - (sourceWidth * ratio)); + break; + default: + destinationX = (int)((width - (sourceWidth * ratio)) / 2); + break; + } + + destinationWidth = (int)Math.Floor(sourceWidth * percentHeight); + } + } + // If height or width is not passed we assume that the standard ratio is to be kept. if (height == 0) { 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 b5a9065fa4..e6d68639f3 100644 --- a/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj +++ b/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_NET45.csproj @@ -188,6 +188,15 @@ ImageProcessor + + + + + + + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 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 2473e62de0..fd59b499d8 100644 --- a/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml +++ b/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml @@ -19,7 +19,7 @@
-

Padded

+

Reside Pad

@@ -28,6 +28,17 @@
+
+
+

Resize Crop

+
+ +
+
+ +
+
+

Filter

diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Web.config b/src/TestWebsites/NET45/Test_Website_NET45/Web.config index 1a9c7f0352..a85d42b443 100644 --- a/src/TestWebsites/NET45/Test_Website_NET45/Web.config +++ b/src/TestWebsites/NET45/Test_Website_NET45/Web.config @@ -12,7 +12,11 @@
- + + + + + @@ -59,33 +63,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/cache.config b/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/cache.config new file mode 100644 index 0000000000..e4a9c5e9a8 --- /dev/null +++ b/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/cache.config @@ -0,0 +1,3 @@ + + + diff --git a/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/processing.config b/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/processing.config new file mode 100644 index 0000000000..1ad37e85a6 --- /dev/null +++ b/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/processing.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/security.config b/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/security.config new file mode 100644 index 0000000000..44fbb5d645 --- /dev/null +++ b/src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/security.config @@ -0,0 +1,8 @@ + + + + + + + +