diff --git a/src/ImageProcessor.UnitTests/Extensions/DoubleExtensionsUnitTests.cs b/src/ImageProcessor.UnitTests/Extensions/DoubleExtensionsUnitTests.cs index cba1c07bc..91b62d74f 100644 --- a/src/ImageProcessor.UnitTests/Extensions/DoubleExtensionsUnitTests.cs +++ b/src/ImageProcessor.UnitTests/Extensions/DoubleExtensionsUnitTests.cs @@ -10,7 +10,6 @@ namespace ImageProcessor.UnitTests.Extensions { - using System.Collections.Generic; using Common.Extensions; using NUnit.Framework; @@ -20,37 +19,20 @@ namespace ImageProcessor.UnitTests.Extensions [TestFixture] public class DoubleExtensionsUnitTests { - /// - /// Stores the values to test for the ToByte() extension method - /// - private Dictionary doubleToByteTests; - - /// - /// Sets up the values for the tests - /// - [TestFixtureSetUp] - public void Init() - { - this.doubleToByteTests = new Dictionary - { - { -10, 0x0 }, - { 1.5, 0x1 }, - { 25.7, 0x19 }, - { 1289047, 0xFF } - }; - } - /// /// Tests the double to byte conversion /// + /// Double input + /// Expected result [Test] - public void TestDoubleToByte() + [TestCase(-10, 0x0)] + [TestCase(1.5, 0x1)] + [TestCase(25.7, 0x19)] + [TestCase(1289047, 0xFF)] + public void TestDoubleToByte(double input, byte expected) { - foreach (var item in this.doubleToByteTests) - { - var result = item.Key.ToByte(); - Assert.AreEqual(item.Value, result); - } + byte result = input.ToByte(); + Assert.AreEqual(expected, result); } } } \ No newline at end of file diff --git a/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj b/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj index 460e329b9..357f3b11d 100644 --- a/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj +++ b/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj @@ -69,6 +69,10 @@ {3b5dd734-fb7a-487d-8ce6-55e7af9aea7e} ImageProcessor + + {2cf69699-959a-44dc-a281-4e2596c25043} + ImageProcessor.Plugins.WebP + diff --git a/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs b/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs index c41454563..db81f4533 100644 --- a/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs +++ b/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs @@ -10,10 +10,14 @@ namespace ImageProcessor.Web.UnitTests { + using System; + using System.Collections.Generic; using System.Drawing; using ImageProcessor.Imaging; using ImageProcessor.Imaging.Filters; using ImageProcessor.Imaging.Formats; + using ImageProcessor.Plugins.WebP.Imaging.Formats; + using NUnit.Framework; /// @@ -25,52 +29,103 @@ namespace ImageProcessor.Web.UnitTests /// /// The alpha regex unit test. /// + /// + /// The input string. + /// + /// + /// The expected result. + /// [Test] - public void TestAlphaRegex() + [TestCase("alpha=66", 66)] + [TestCase("alpha=-66", 66)] + [TestCase("alpha=101", 1)] + [TestCase("alpha=-101", 1)] + [TestCase("alpha=000053", 53)] + public void TestAlphaRegex(string input, int expected) { - const string Querystring = "alpha=56"; - const int Expected = 56; - Processors.Alpha alpha = new Processors.Alpha(); - alpha.MatchRegexIndex(Querystring); + alpha.MatchRegexIndex(input); + int result = alpha.Processor.DynamicParameter; - int actual = alpha.Processor.DynamicParameter; - - Assert.AreEqual(Expected, actual); + Assert.AreEqual(expected, result); } /// - /// The brightness regex unit test. + /// The contrast regex unit test. /// + /// + /// The input string. + /// + /// + /// The expected result. + /// [Test] - public void TestBrightnessRegex() + [TestCase("brightness=56", 56)] + [TestCase("brightness=84", 84)] + [TestCase("brightness=66", 66)] + [TestCase("brightness=101", 1)] + [TestCase("brightness=00001", 1)] + [TestCase("brightness=-50", -50)] + [TestCase("brightness=0", 0)] + public void TestBrightnesstRegex(string input, int expected) { - const string Querystring = "brightness=56"; - const int Expected = 56; - Processors.Brightness brightness = new Processors.Brightness(); - brightness.MatchRegexIndex(Querystring); - - int actual = brightness.Processor.DynamicParameter; + brightness.MatchRegexIndex(input); + int result = brightness.Processor.DynamicParameter; - Assert.AreEqual(Expected, actual); + Assert.AreEqual(expected, result); } /// /// The contrast regex unit test. /// + /// + /// The input string. + /// + /// + /// The expected result. + /// [Test] - public void TestContrastRegex() + [TestCase("contrast=56", 56)] + [TestCase("contrast=84", 84)] + [TestCase("contrast=66", 66)] + [TestCase("contrast=101", 1)] + [TestCase("contrast=00001", 1)] + [TestCase("contrast=-50", -50)] + [TestCase("contrast=0", 0)] + public void TestContrastRegex(string input, int expected) { - const string Querystring = "contrast=56"; - const int Expected = 56; - Processors.Contrast contrast = new Processors.Contrast(); - contrast.MatchRegexIndex(Querystring); + contrast.MatchRegexIndex(input); + int result = contrast.Processor.DynamicParameter; - int actual = contrast.Processor.DynamicParameter; + Assert.AreEqual(expected, result); + } - Assert.AreEqual(Expected, actual); + /// + /// The saturation regex unit test. + /// + /// + /// The input string. + /// + /// + /// The expected result. + /// + [Test] + [TestCase("saturation=56", 56)] + [TestCase("saturation=84", 84)] + [TestCase("saturation=66", 66)] + [TestCase("saturation=101", 1)] + [TestCase("saturation=00001", 1)] + [TestCase("saturation=-50", -50)] + [TestCase("saturation=0", 0)] + public void TestSaturationRegex(string input, int expected) + { + Processors.Saturation saturation = new Processors.Saturation(); + saturation.MatchRegexIndex(input); + int result = saturation.Processor.DynamicParameter; + + Assert.AreEqual(expected, result); } /// @@ -93,52 +148,102 @@ namespace ImageProcessor.Web.UnitTests /// The filter regex unit test. /// [Test] + public void TestFilterRegex() { - // Should really write more for the other filters. - const string Querystring = "filter=lomograph"; - IMatrixFilter expected = MatrixFilters.Lomograph; + Dictionary data = new Dictionary + { + { + "filter=lomograph", MatrixFilters.Lomograph + }, + { + "filter=polaroid", MatrixFilters.Polaroid + }, + { + "filter=comic", MatrixFilters.Comic + }, + { + "filter=greyscale", MatrixFilters.GreyScale + }, + { + "filter=blackwhite", MatrixFilters.BlackWhite + }, + { + "filter=invert", MatrixFilters.Invert + }, + { + "filter=gotham", MatrixFilters.Gotham + }, + { + "filter=hisatch", MatrixFilters.HiSatch + }, + { + "filter=losatch", MatrixFilters.LoSatch + }, + { + "filter=sepia", MatrixFilters.Sepia + } + }; Processors.Filter filter = new Processors.Filter(); - filter.MatchRegexIndex(Querystring); - - IMatrixFilter actual = filter.Processor.DynamicParameter; - - Assert.AreEqual(expected, actual); + foreach (KeyValuePair item in data) + { + filter.MatchRegexIndex(item.Key); + IMatrixFilter result = filter.Processor.DynamicParameter; + Assert.AreEqual(item.Value, result); + } } /// /// The format regex unit test. /// + /// + /// The input querystring. + /// + /// + /// The expected type. + /// [Test] - public void TestFormatRegex() + [TestCase("format=bmp", typeof(BitmapFormat))] + [TestCase("format=png", typeof(PngFormat))] + [TestCase("format=png8", typeof(PngFormat))] + [TestCase("format=jpeg", typeof(JpegFormat))] + [TestCase("format=jpg", typeof(JpegFormat))] + [TestCase("format=gif", typeof(GifFormat))] + [TestCase("format=webp", typeof(WebPFormat))] + public void TestFormatRegex(string input, Type expected) { - const string Querystring = "format=gif"; - ISupportedImageFormat expected = new GifFormat(); - Processors.Format format = new Processors.Format(); - format.MatchRegexIndex(Querystring); + format.MatchRegexIndex(input); + Type result = format.Processor.DynamicParameter.GetType(); - ISupportedImageFormat actual = format.Processor.DynamicParameter; - - Assert.AreEqual(expected, actual); + Assert.AreEqual(expected, result); } /// /// The quality regex unit test. /// + /// + /// The input. + /// + /// + /// The expected result. + /// [Test] - public void TestQualityRegex() + [TestCase("quality=56", 56)] + [TestCase("quality=84", 84)] + [TestCase("quality=66", 66)] + [TestCase("quality=101", 1)] + [TestCase("quality=00001", 1)] + [TestCase("quality=-50", 50)] + [TestCase("quality=0", 0)] + public void TestQualityRegex(string input, int expected) { - const string Querystring = "quality=56"; - const int Expected = 56; - Processors.Quality quality = new Processors.Quality(); - quality.MatchRegexIndex(Querystring); + quality.MatchRegexIndex(input); + int result = quality.Processor.DynamicParameter; - int actual = quality.Processor.DynamicParameter; - - Assert.AreEqual(Expected, actual); + Assert.AreEqual(expected, result); } /// @@ -161,18 +266,25 @@ namespace ImageProcessor.Web.UnitTests /// /// The rotate regex unit test. /// + /// + /// The input string. + /// + /// + /// The expected result. + /// [Test] - public void TestRotateRegex() + [TestCase("rotate=0", 0)] + [TestCase("rotate=270", 270)] + [TestCase("rotate=-270", 0)] + [TestCase("rotate=angle-28", 28)] + public void TestRotateRegex(string input, int expected) { - const string Querystring = "rotate=270"; - const int Expected = 270; - Processors.Rotate rotate = new Processors.Rotate(); - rotate.MatchRegexIndex(Querystring); + rotate.MatchRegexIndex(input); - int actual = rotate.Processor.DynamicParameter; + int result = rotate.Processor.DynamicParameter; - Assert.AreEqual(Expected, actual); + Assert.AreEqual(expected, result); } /// @@ -181,14 +293,26 @@ namespace ImageProcessor.Web.UnitTests [Test] public void TestRoundedCornersRegex() { - const string Querystring = "roundedcorners=30"; - RoundedCornerLayer expected = new RoundedCornerLayer(30, true, true, true, true); - Processors.RoundedCorners roundedCorners = new Processors.RoundedCorners(); - roundedCorners.MatchRegexIndex(Querystring); + Dictionary data = new Dictionary + { + { + "roundedcorners=30", new RoundedCornerLayer(30, true, true, true, true) + }, + { + "roundedcorners=radius-26|tl-true|tr-false|bl-true|br-false", new RoundedCornerLayer(26, true, false, true, false) + }, + { + "roundedcorners=26,tl=true,tr=false,bl=true,br=false", new RoundedCornerLayer(26, true, false, true, false) + } + }; - RoundedCornerLayer actual = roundedCorners.Processor.DynamicParameter; - - Assert.AreEqual(expected, actual); + Processors.RoundedCorners roundedCorners = new Processors.RoundedCorners(); + foreach (KeyValuePair item in data) + { + roundedCorners.MatchRegexIndex(item.Key); + RoundedCornerLayer result = roundedCorners.Processor.DynamicParameter; + Assert.AreEqual(item.Value, result); + } } /// @@ -197,20 +321,29 @@ namespace ImageProcessor.Web.UnitTests [Test] public void TestTintRegex() { - const string HexQuerystring = "tint=6aa6cc"; - const string RgbaQuerystring = "tint=106,166,204,255"; - Color expectedHex = ColorTranslator.FromHtml("#" + "6aa6cc"); - Color expectedRgba = Color.FromArgb(255, 106, 166, 204); + Dictionary data = new Dictionary + { + { + "tint=6aa6cc", ColorTranslator.FromHtml("#" + "6aa6cc") + }, + { + "tint=106,166,204,255", Color.FromArgb(255, 106, 166, 204) + }, + { + "tint=fff", Color.FromArgb(255, 255, 255, 255) + }, + { + "tint=white", Color.White + } + }; Processors.Tint tint = new Processors.Tint(); - tint.MatchRegexIndex(HexQuerystring); - Color actualHex = tint.Processor.DynamicParameter; - Assert.AreEqual(expectedHex, actualHex); - - tint = new Processors.Tint(); - tint.MatchRegexIndex(RgbaQuerystring); - Color actualRgba = tint.Processor.DynamicParameter; - Assert.AreEqual(expectedRgba, actualRgba); + foreach (KeyValuePair item in data) + { + tint.MatchRegexIndex(item.Key); + Color result = tint.Processor.DynamicParameter; + Assert.AreEqual(item.Value, result); + } } } } \ No newline at end of file diff --git a/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs b/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs index 505d80d99..3e8362d08 100644 --- a/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs +++ b/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs @@ -11,8 +11,10 @@ namespace ImageProcessor.Web.Helpers { using System; + using System.Collections.Generic; using System.Drawing; using System.Globalization; + using System.Text; using System.Text.RegularExpressions; using ImageProcessor.Common.Extensions; @@ -24,10 +26,15 @@ namespace ImageProcessor.Web.Helpers /// public static class CommonParameterParserUtility { + /// + /// The collection of known colors. + /// + private static readonly Dictionary KnownColors = new Dictionary(); + /// /// The regular expression to search strings for colors. /// - private static readonly Regex ColorRegex = new Regex(@"(bgcolor|color|tint|vignette)(=|-)(\d+,\d+,\d+,\d+|([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled); + private static readonly Regex ColorRegex = BuildColorRegex(); /// /// The regular expression to search strings for angles. @@ -37,7 +44,7 @@ namespace ImageProcessor.Web.Helpers /// /// The regular expression to search strings for values between 1 and 100. /// - private static readonly Regex In100RangeRegex = new Regex(@"(-?(?:100)|-?([1-9]?[0-9]))", RegexOptions.Compiled); + private static readonly Regex In100RangeRegex = new Regex(@"(-?(0*(?:[1-9][0-9]?|100)))", RegexOptions.Compiled); /// /// The sharpen regex. @@ -93,6 +100,11 @@ namespace ImageProcessor.Web.Helpers { string value = match.Value.Split(new[] { '=', '-' })[1]; + if (KnownColors.ContainsKey(value)) + { + return Color.FromKnownColor(KnownColors[value]); + } + if (value.Contains(",")) { int[] split = value.ToPositiveIntegerArray(); @@ -224,5 +236,33 @@ namespace ImageProcessor.Web.Helpers return 0; } + + /// + /// Builds a regular expression for the three main colour types. + /// + /// + /// The to match colors. + /// + private static Regex BuildColorRegex() + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append(@"(bgcolor|color|tint|vignette)(=|-)(\d+,\d+,\d+,\d+|([0-9a-fA-F]{3}){1,2}|("); + + KnownColor[] knownColors = (KnownColor[])Enum.GetValues(typeof(KnownColor)); + + for (int i = 0; i < knownColors.Length; i++) + { + KnownColor knownColor = knownColors[i]; + string name = knownColor.ToString().ToLowerInvariant(); + + KnownColors.Add(name, knownColor); + + stringBuilder.Append(i > 0 ? "|" + name : name); + } + + stringBuilder.Append("))"); + + return new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase); + } } } diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs index 2fe0a275d..d7b15e7e1 100644 --- a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs +++ b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs @@ -43,6 +43,11 @@ namespace ImageProcessor.Web.HttpModules /// private const string CachedResponseTypeKey = "CACHED_IMAGE_RESPONSE_TYPE_054F217C-11CF-49FF-8D2F-698E8E6EB58F"; + /// + /// The key for storing the file dependency of the current image. + /// + private const string CachedResponseFileDependency = "CACHED_IMAGE_DEPENDENCY_054F217C-11CF-49FF-8D2F-698E8E6EB58F"; + /// /// The regular expression to search strings for. /// @@ -215,15 +220,18 @@ namespace ImageProcessor.Web.HttpModules HttpContext context = ((HttpApplication)sender).Context; object responseTypeObject = context.Items[CachedResponseTypeKey]; + object dependencyFileObject = context.Items[CachedResponseFileDependency]; - if (responseTypeObject != null) + if (responseTypeObject != null && dependencyFileObject != null) { string responseType = (string)responseTypeObject; + string dependencyFile = (string)dependencyFileObject; // Set the headers - this.SetHeaders(context, responseType); + this.SetHeaders(context, responseType, dependencyFile); context.Items[CachedResponseTypeKey] = null; + context.Items[CachedResponseFileDependency] = null; } } @@ -440,7 +448,15 @@ namespace ImageProcessor.Web.HttpModules } } - string incomingEtag = context.Request.Headers["If-None-Match"]; + // Image is from the cache so the mime-type will need to be set. + if (context.Items[CachedResponseTypeKey] == null) + { + context.Items[CachedResponseTypeKey] = ImageHelpers.GetExtension(cachedPath).Replace(".", "image/"); + } + + context.Items[CachedResponseFileDependency] = cachedPath; + + string incomingEtag = context.Request.Headers["If" + "-None-Match"]; if (incomingEtag != null && !isNewOrUpdated) { @@ -449,8 +465,7 @@ namespace ImageProcessor.Web.HttpModules context.Response.AddHeader("Content-Length", "0"); context.Response.StatusCode = (int)HttpStatusCode.NotModified; context.Response.SuppressContent = true; - context.Response.AddFileDependency(context.Server.MapPath(virtualCachedPath)); - this.SetHeaders(context, (string)context.Items[CachedResponseTypeKey]); + this.SetHeaders(context, (string)context.Items[CachedResponseTypeKey], cachedPath); if (!isRemote) { @@ -482,18 +497,28 @@ namespace ImageProcessor.Web.HttpModules /// the HttpContext object that provides /// references to the intrinsic server objects /// - /// The HTTP MIME type to to send. - private void SetHeaders(HttpContext context, string responseType) + /// + /// The HTTP MIME type to to send. + /// + /// + /// The dependency path for the cache dependency. + /// + private void SetHeaders(HttpContext context, string responseType, string dependencyPath) { HttpResponse response = context.Response; response.ContentType = responseType; - response.AddHeader("Image-Served-By", "ImageProcessor.Web/" + AssemblyVersion); + if (response.Headers["Image-Served-By"] == null) + { + response.AddHeader("Image-Served-By", "ImageProcessor.Web/" + AssemblyVersion); + } HttpCachePolicy cache = response.Cache; cache.SetCacheability(HttpCacheability.Public); cache.VaryByHeaders["Accept-Encoding"] = true; + + context.Response.AddFileDependency(dependencyPath); cache.SetLastModifiedFromFileDependencies(); int maxDays = DiskCache.MaxFileCachedDuration; diff --git a/src/ImageProcessor.Web/NET45/Processors/Format.cs b/src/ImageProcessor.Web/NET45/Processors/Format.cs index b419f211f..7b57d7ddd 100644 --- a/src/ImageProcessor.Web/NET45/Processors/Format.cs +++ b/src/ImageProcessor.Web/NET45/Processors/Format.cs @@ -15,7 +15,6 @@ namespace ImageProcessor.Web.Processors using System.Linq; using System.Text; using System.Text.RegularExpressions; - using System.Web; using ImageProcessor.Configuration; using ImageProcessor.Imaging.Formats; diff --git a/src/ImageProcessor.sln b/src/ImageProcessor.sln index a12d17c68..8ad0ed4e9 100644 --- a/src/ImageProcessor.sln +++ b/src/ImageProcessor.sln @@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor.Web.UnitTest EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor.UnitTests", "ImageProcessor.UnitTests\ImageProcessor.UnitTests.csproj", "{633B1C4C-4823-47BE-9A01-A665F3118C8C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor.Plugins.WebP", "Plugins\ImageProcessor\ImageProcessor.Plugins.WebP\ImageProcessor.Plugins.WebP.csproj", "{2CF69699-959A-44DC-A281-4E2596C25043}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution All|Any CPU = All|Any CPU @@ -201,6 +203,21 @@ Global {633B1C4C-4823-47BE-9A01-A665F3118C8C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {633B1C4C-4823-47BE-9A01-A665F3118C8C}.Release|Mixed Platforms.Build.0 = Release|Any CPU {633B1C4C-4823-47BE-9A01-A665F3118C8C}.Release|x86.ActiveCfg = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.All|Any CPU.ActiveCfg = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.All|Any CPU.Build.0 = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.All|Mixed Platforms.ActiveCfg = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.All|Mixed Platforms.Build.0 = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.All|x86.ActiveCfg = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Debug|x86.ActiveCfg = Debug|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Release|Any CPU.Build.0 = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2CF69699-959A-44DC-A281-4E2596C25043}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs b/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs index 09b772455..4cffe51eb 100644 --- a/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs +++ b/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs @@ -4,22 +4,25 @@ // Licensed under the Apache License, Version 2.0. // // -// The image processor bootstrapper. +// The ImageProcessor bootstrapper. // // -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Configuration { using System; + using System.Collections; using System.Collections.Generic; + using System.IO; using System.Linq; + using System.Reflection; using ImageProcessor.Common.Exceptions; using ImageProcessor.Common.Extensions; using ImageProcessor.Imaging.Formats; /// - /// The image processor bootstrapper. + /// The ImageProcessor bootstrapper. /// public class ImageProcessorBootstrapper { @@ -35,8 +38,8 @@ namespace ImageProcessor.Configuration /// private ImageProcessorBootstrapper() { - this.LoadSupportedImageFormats(); this.NativeBinaryFactory = new NativeBinaryFactory(); + this.LoadSupportedImageFormats(); } /// @@ -70,14 +73,36 @@ namespace ImageProcessor.Configuration try { Type type = typeof(ISupportedImageFormat); + + // Get any referenced but not used assemblies. + Assembly executingAssembly = Assembly.GetExecutingAssembly(); + string targetBasePath = Path.GetDirectoryName(new Uri(executingAssembly.Location).LocalPath); + + // ReSharper disable once AssignNullToNotNullAttribute + FileInfo[] files = new DirectoryInfo(targetBasePath).GetFiles("*.dll", SearchOption.AllDirectories); + + HashSet found = new HashSet(); + foreach (FileInfo fileInfo in files) + { + AssemblyName assemblyName = AssemblyName.GetAssemblyName(fileInfo.FullName); + + if (!AppDomain.CurrentDomain.GetAssemblies() + .Any(a => AssemblyName.ReferenceMatchesDefinition(assemblyName, a.GetName()))) + { + // In a web app, this assembly will automatically be bound from the + // Asp.Net Temporary folder from where the site actually runs. + this.LoadReferencedAssemblies(found, Assembly.Load(assemblyName)); + } + } + List availableTypes = AppDomain.CurrentDomain .GetAssemblies() - .SelectMany(s => s.GetLoadableTypes()) + .SelectMany(a => a.GetLoadableTypes()) .Where(t => type.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract) .ToList(); this.SupportedImageFormats = availableTypes - .Select(x => (Activator.CreateInstance(x) as ISupportedImageFormat)).ToList(); + .Select(f => (Activator.CreateInstance(f) as ISupportedImageFormat)).ToList(); } catch (Exception ex) { @@ -85,5 +110,49 @@ namespace ImageProcessor.Configuration } } } + + /// + /// Loads any referenced assemblies into the current application domain. + /// + /// + /// The collection containing the name of already found assemblies. + /// + /// + /// The assembly to load from. + /// + private void LoadReferencedAssemblies(HashSet found, Assembly assembly) + { + // Used to avoid duplicates + ArrayList results = new ArrayList(); + + // Resulting info + Stack stack = new Stack(); + + // Stack of names + // Store root assembly (level 0) directly into results list + stack.Push(assembly.ToString()); + + // Do a preorder, non-recursive traversal + while (stack.Count > 0) + { + string info = (string)stack.Pop(); + + // Get next assembly + if (!found.Contains(info)) + { + found.Add(info); + results.Add(info); + + // Store it to results ArrayList + Assembly child = Assembly.Load(info); + AssemblyName[] subchild = child.GetReferencedAssemblies(); + + for (int i = subchild.Length - 1; i >= 0; --i) + { + stack.Push(subchild[i].ToString()); + } + } + } + } } } diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index ad69d127b..ed1aa9c64 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -79,7 +79,6 @@ - @@ -87,7 +86,6 @@ - @@ -131,10 +129,6 @@ - - - - diff --git a/src/ImageProcessorConsole/ImageProcessorConsole.csproj b/src/ImageProcessorConsole/ImageProcessorConsole.csproj index fba366b0c..1afd8ef0e 100644 --- a/src/ImageProcessorConsole/ImageProcessorConsole.csproj +++ b/src/ImageProcessorConsole/ImageProcessorConsole.csproj @@ -56,6 +56,10 @@ {3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E} ImageProcessor + + {2cf69699-959a-44dc-a281-4e2596c25043} + ImageProcessor.Plugins.WebP + + \ No newline at end of file diff --git a/src/ImageProcessor/Imaging/Formats/NativeMethods.cs b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Imaging/Formats/NativeMethods.cs similarity index 96% rename from src/ImageProcessor/Imaging/Formats/NativeMethods.cs rename to src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Imaging/Formats/NativeMethods.cs index 0c42710b0..936b58256 100644 --- a/src/ImageProcessor/Imaging/Formats/NativeMethods.cs +++ b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Imaging/Formats/NativeMethods.cs @@ -8,7 +8,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessor.Imaging.Formats +namespace ImageProcessor.Plugins.WebP.Imaging.Formats { using System; using System.IO; @@ -23,7 +23,7 @@ namespace ImageProcessor.Imaging.Formats internal static class NativeMethods { /// - /// Whether the process is running in 64bit mode. Used for calling the correct dllimport method. + /// Whether the process is running in 64bit mode. Used for calling the correct method. /// Clunky I know but I couldn't get dynamic methods to work. /// private static readonly bool Is64Bit = Environment.Is64BitProcess; @@ -34,7 +34,7 @@ namespace ImageProcessor.Imaging.Formats static NativeMethods() { string folder = Is64Bit ? "x64" : "x86"; - string name = string.Format("ImageProcessor.Resources.Unmanaged.{0}.libwebp.dll", folder); + string name = string.Format("ImageProcessor.Plugins.WebP.Resources.Unmanaged.{0}.libwebp.dll", folder); Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name); using (MemoryStream memoryStream = new MemoryStream()) diff --git a/src/ImageProcessor/Imaging/Formats/WebPFormat.cs b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Imaging/Formats/WebPFormat.cs similarity index 98% rename from src/ImageProcessor/Imaging/Formats/WebPFormat.cs rename to src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Imaging/Formats/WebPFormat.cs index 9f350c5c0..0bc2e2b7b 100644 --- a/src/ImageProcessor/Imaging/Formats/WebPFormat.cs +++ b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Imaging/Formats/WebPFormat.cs @@ -10,7 +10,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessor.Imaging.Formats +namespace ImageProcessor.Plugins.WebP.Imaging.Formats { using System; using System.Collections.Generic; @@ -22,6 +22,7 @@ namespace ImageProcessor.Imaging.Formats using System.Text; using ImageProcessor.Common.Exceptions; + using ImageProcessor.Imaging.Formats; /// /// Provides the necessary information to support webp images. diff --git a/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Properties/AssemblyInfo.cs b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d9aa6f985 --- /dev/null +++ b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ImageProcessor.Plugins.WebP")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("James South")] +[assembly: AssemblyProduct("ImageProcessor.Plugins.WebP")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bf160db5-2ea7-4c85-9b0e-f1ddf2595e37")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/ImageProcessor/Resources/Unmanaged/x64/libwebp.dll.REMOVED.git-id b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Resources/Unmanaged/x64/libwebp.dll.REMOVED.git-id similarity index 100% rename from src/ImageProcessor/Resources/Unmanaged/x64/libwebp.dll.REMOVED.git-id rename to src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Resources/Unmanaged/x64/libwebp.dll.REMOVED.git-id diff --git a/src/ImageProcessor/Resources/Unmanaged/x86/libwebp.dll.REMOVED.git-id b/src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Resources/Unmanaged/x86/libwebp.dll.REMOVED.git-id similarity index 100% rename from src/ImageProcessor/Resources/Unmanaged/x86/libwebp.dll.REMOVED.git-id rename to src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Resources/Unmanaged/x86/libwebp.dll.REMOVED.git-id diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Controllers/HomeController.cs b/src/TestWebsites/NET45/Test_Website_NET45/Controllers/HomeController.cs index 02fc35575..1eae92d44 100644 --- a/src/TestWebsites/NET45/Test_Website_NET45/Controllers/HomeController.cs +++ b/src/TestWebsites/NET45/Test_Website_NET45/Controllers/HomeController.cs @@ -41,6 +41,11 @@ namespace Test_Website_NET45.Controllers return View(); } + public ActionResult WebP() + { + return View(); + } + public ActionResult External() { return this.View(); diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_MVC_NET45.csproj b/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_MVC_NET45.csproj index 96f09d69b..c2118914f 100644 --- a/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_MVC_NET45.csproj +++ b/src/TestWebsites/NET45/Test_Website_NET45/Test_Website_MVC_NET45.csproj @@ -159,6 +159,10 @@ {3b5dd734-fb7a-487d-8ce6-55e7af9aea7e} ImageProcessor + + {2cf69699-959a-44dc-a281-4e2596c25043} + ImageProcessor.Plugins.WebP + @@ -191,6 +195,9 @@ + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/WebP.cshtml b/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/WebP.cshtml new file mode 100644 index 000000000..ff03d1240 --- /dev/null +++ b/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/WebP.cshtml @@ -0,0 +1,100 @@ +@{ + ViewBag.Title = "WebP"; +} + +
+

WebP

+
+
+
+

Resized

+ +
+
+

Cropped

+ +
+
+
+
+

Filter

+
+
+

blackwhite

+ +
+
+

comic

+ +
+
+
+
+

lomograph

+ +
+
+

greyscale

+ +
+
+
+
+

polaroid

+ +
+
+

sepia

+ +
+
+
+
+

gotham

+ +
+
+

hisatch

+ +
+
+
+
+

losatch

+ +
+
+
+
+
+
+

Watermark

+ +
+
+

Format

+ +
+
+
+
+
+
+

Rotate

+ +
+
+

Quality

+ +
+
+
+
+
+
+

Alpha

+ +
+
+
+
diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Views/Shared/_Layout.cshtml b/src/TestWebsites/NET45/Test_Website_NET45/Views/Shared/_Layout.cshtml index 74af019a2..5f8255ea4 100644 --- a/src/TestWebsites/NET45/Test_Website_NET45/Views/Shared/_Layout.cshtml +++ b/src/TestWebsites/NET45/Test_Website_NET45/Views/Shared/_Layout.cshtml @@ -29,6 +29,7 @@
  • @Html.ActionLink("Png8", "Png8")
  • @Html.ActionLink("Bmp", "Bmp")
  • @Html.ActionLink("Tiff", "Tiff")
  • +
  • @Html.ActionLink("WebP", "WebP")
  • @Html.ActionLink("External", "External")