From 51ab0032b343b4243f2d3aed70d19b90d8b4b69d Mon Sep 17 00:00:00 2001 From: James South Date: Thu, 30 Oct 2014 21:12:14 +0000 Subject: [PATCH] Adding mask to web Former-commit-id: 44bffdc11075f2b00cd57ffa109823544f015db3 Former-commit-id: b364cd3158ac10fa101a5b5cb5fb85aa9d943b29 --- .../Images/masks/mask.png | Bin 0 -> 3284 bytes .../Configuration/Resources/processing.config | 7 +- .../Helpers/ImageHelpers.cs | 14 +- .../ImageProcessor.Web.csproj | 1 + src/ImageProcessor.Web/Processors/Mask.cs | 185 ++++++++++++++++++ src/TestWebsites/MVC/Web.config | 4 +- 6 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 src/ImageProcessor.UnitTests/Images/masks/mask.png create mode 100644 src/ImageProcessor.Web/Processors/Mask.cs diff --git a/src/ImageProcessor.UnitTests/Images/masks/mask.png b/src/ImageProcessor.UnitTests/Images/masks/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..f86272dc302b50135b7efcad7548f89e339abf86 GIT binary patch literal 3284 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 Invw4Q2YL@68UO$Q literal 0 HcmV?d00001 diff --git a/src/ImageProcessor.Web/Configuration/Resources/processing.config b/src/ImageProcessor.Web/Configuration/Resources/processing.config index a1fed5fbdd..9d4a463090 100644 --- a/src/ImageProcessor.Web/Configuration/Resources/processing.config +++ b/src/ImageProcessor.Web/Configuration/Resources/processing.config @@ -12,7 +12,7 @@ - + @@ -27,6 +27,11 @@ + + + + + diff --git a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs index 2b7946d779..29d7953a40 100644 --- a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs +++ b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs @@ -24,7 +24,13 @@ namespace ImageProcessor.Web.Helpers /// /// The regex pattern. /// - private static readonly string ExtensionRegexPattern = BuildExtensionRegexPattern(); + public static readonly string ExtensionRegexPattern = BuildExtensionRegexPattern(); + + /// + /// The exclude regex for matching things to ignore when parsing image extensions. + /// I'd like to make something more extensible than this. + /// + private static readonly Regex ExcludeRegex = new Regex(@"mask=[\w+-]+.", RegexOptions.IgnoreCase); /// /// The image format regex. @@ -57,6 +63,12 @@ namespace ImageProcessor.Web.Helpers /// public static string GetExtension(string input) { + // First filter out any troublesome elements. + foreach (Match exlude in ExcludeRegex.Matches(input)) + { + input = input.Replace(exlude.Value, string.Empty); + } + Match match = FormatRegex.Match(input); if (match.Success) diff --git a/src/ImageProcessor.Web/ImageProcessor.Web.csproj b/src/ImageProcessor.Web/ImageProcessor.Web.csproj index c8171d4ba2..98f0385af3 100644 --- a/src/ImageProcessor.Web/ImageProcessor.Web.csproj +++ b/src/ImageProcessor.Web/ImageProcessor.Web.csproj @@ -48,6 +48,7 @@ + diff --git a/src/ImageProcessor.Web/Processors/Mask.cs b/src/ImageProcessor.Web/Processors/Mask.cs new file mode 100644 index 0000000000..f5ad7d454c --- /dev/null +++ b/src/ImageProcessor.Web/Processors/Mask.cs @@ -0,0 +1,185 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Applies a mask to the given image. If the mask is not the same size as the image +// it will be centered against the image. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.Processors +{ + using System; + using System.Drawing; + using System.IO; + using System.Text; + using System.Text.RegularExpressions; + using System.Web.Hosting; + + using ImageProcessor.Processors; + using ImageProcessor.Web.Extensions; + using ImageProcessor.Web.Helpers; + + /// + /// Applies a mask to the given image. If the mask is not the same size as the image + /// it will be centered against the image. + /// + public class Mask : IWebGraphicsProcessor + { + /// + /// The regular expression to search strings for. + /// + private static readonly Regex QueryRegex = new Regex(@"(mask=|maskposition=)[^&]+", RegexOptions.Compiled); + + /// + /// The mask image regex. + /// + private static readonly Regex PixelRegex = new Regex(@"mask=[\w+-]+." + ImageHelpers.ExtensionRegexPattern); + + /// + /// The point regex. + /// + private static readonly Regex PointRegex = new Regex(@"maskposition=\d+,\d+", RegexOptions.Compiled); + + /// + /// Initializes a new instance of the class. + /// + public Mask() + { + this.Processor = new ImageProcessor.Processors.Mask(); + } + + /// + /// Gets the regular expression to search strings for. + /// + public Regex RegexPattern + { + get + { + return QueryRegex; + } + } + + /// + /// Gets the order in which this processor is to be used in a chain. + /// + public int SortOrder { get; private set; } + + /// + /// Gets the associated graphics processor. + /// + public IGraphicsProcessor Processor { get; private 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 int MatchRegexIndex(string queryString) + { + int index = 0; + + // Set the sort order to max to allow filtering. + this.SortOrder = int.MaxValue; + + // First merge the matches so we can parse . + StringBuilder stringBuilder = new StringBuilder(); + + foreach (Match match in this.RegexPattern.Matches(queryString)) + { + if (match.Success) + { + if (index == 0) + { + // Set the index on the first instance only. + this.SortOrder = match.Index; + } + + stringBuilder.Append(match.Value); + + index += 1; + } + } + + if (this.SortOrder < int.MaxValue) + { + // Match syntax + string toParse = stringBuilder.ToString(); + Image image = this.ParseImage(toParse); + Point? rectangle = this.ParsePoint(toParse); + this.Processor.DynamicParameter = new Tuple(image, rectangle); + } + + return this.SortOrder; + } + + /// + /// Returns the correct size of pixels. + /// + /// + /// The input containing the value to parse. + /// + /// + /// The representing the pixel size. + /// + public Image ParseImage(string input) + { + Image image = null; + + // Correctly parse the path. + string path; + this.Processor.Settings.TryGetValue("VirtualPath", out path); + + if (!string.IsNullOrWhiteSpace(path) && path.StartsWith("~/")) + { + Match match = PixelRegex.Match(input); + + if (match.Success) + { + string imagePath = HostingEnvironment.MapPath(path); + if (imagePath != null) + { + imagePath = Path.Combine(imagePath, match.Value.Split('=')[1]); + using (ImageFactory factory = new ImageFactory()) + { + factory.Load(imagePath); + image = new Bitmap(factory.Image); + } + } + } + } + + return image; + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct + /// + private Point? ParsePoint(string input) + { + int[] dimensions = { }; + + foreach (Match match in PointRegex.Matches(input)) + { + dimensions = match.Value.ToPositiveIntegerArray(); + } + + if (dimensions.Length == 2) + { + return new Point(dimensions[0], dimensions[1]); + } + + return null; + } + } +} diff --git a/src/TestWebsites/MVC/Web.config b/src/TestWebsites/MVC/Web.config index 9bef44eafd..5aae58e7ed 100644 --- a/src/TestWebsites/MVC/Web.config +++ b/src/TestWebsites/MVC/Web.config @@ -5,7 +5,7 @@ --> - +