From 9e67f4b9819063352ed0e1ace04a956f973ae865 Mon Sep 17 00:00:00 2001 From: James South Date: Sun, 27 Jul 2014 23:07:11 +0100 Subject: [PATCH 1/7] Adding Linux native dll loading Former-commit-id: bf4c36486c3e3db4883d766162c4cf5c3c32da74 --- .../Configuration/NativeBinaryFactory.cs | 11 ++++++- .../Configuration/NativeMethods.cs | 28 ++++++++++++++++++ .../images/output/rotate.jpg | Bin 0 -> 26227 bytes 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/ImageProcessorConsole/images/output/rotate.jpg diff --git a/src/ImageProcessor/Configuration/NativeBinaryFactory.cs b/src/ImageProcessor/Configuration/NativeBinaryFactory.cs index 741da61e2..e210aa036 100644 --- a/src/ImageProcessor/Configuration/NativeBinaryFactory.cs +++ b/src/ImageProcessor/Configuration/NativeBinaryFactory.cs @@ -94,7 +94,7 @@ namespace ImageProcessor.Configuration Assembly assembly = Assembly.GetExecutingAssembly(); string targetBasePath = new Uri(assembly.Location).LocalPath; string targetPath = Path.GetFullPath(Path.Combine(targetBasePath, "..\\" + folder + "\\" + name)); - + // Copy the file across if necessary. FileInfo fileInfo = new FileInfo(targetPath); bool rewrite = true; @@ -122,8 +122,13 @@ namespace ImageProcessor.Configuration try { +#if !__MonoCS__ // Load the binary into memory. pointer = NativeMethods.LoadLibrary(targetPath); +#else + // Load the binary into memory. The second parameter forces it to load immediately. + pointer = NativeMethods.dlopen(targetPath, 2); +#endif } catch (Exception ex) { @@ -187,9 +192,13 @@ namespace ImageProcessor.Configuration { IntPtr pointer = nativeBinary.Value; +#if !__MonoCS__ // According to http://stackoverflow.com/a/2445558/427899 you need to call this twice. NativeMethods.FreeLibrary(pointer); NativeMethods.FreeLibrary(pointer); +#else + NativeMethods.dlclose(pointer); +#endif } } } diff --git a/src/ImageProcessor/Configuration/NativeMethods.cs b/src/ImageProcessor/Configuration/NativeMethods.cs index a20b5a86f..6c1ab9805 100644 --- a/src/ImageProcessor/Configuration/NativeMethods.cs +++ b/src/ImageProcessor/Configuration/NativeMethods.cs @@ -40,5 +40,33 @@ namespace ImageProcessor.Configuration /// If the function succeeds, the return value is nonzero; otherwise zero. [DllImport("kernel32", CharSet = CharSet.Auto)] public static extern bool FreeLibrary(IntPtr hModule); + + /// + /// Loads the specified module into the address space of the calling process. + /// The specified module may cause other modules to be loaded. + /// + /// + /// The name of the module. This can be either a library module or + /// an executable module. + /// + /// + /// The flag indicating whether to load the library immediately or lazily. + /// + /// + /// If the function succeeds, the return value is a handle to the module; otherwise null. + /// + [System.Runtime.InteropServices.DllImport("libdl")] + public static extern IntPtr dlopen(string libname, int flags); + + /// + /// Frees the loaded dynamic-link library (DLL) module and, if necessary, decrements its reference count. + /// When the reference count reaches zero, the module is unloaded from the address space of the calling + /// process and the handle is no longer valid. + /// + /// A handle to the loaded library module. + /// The LoadLibrary, LoadLibraryEx, GetModuleHandle, or GetModuleHandleEx function returns this handle. + /// If the function succeeds, the return value is nonzero; otherwise zero. + [System.Runtime.InteropServices.DllImport("libdl")] + public static extern int dlclose(IntPtr hModule); } } diff --git a/src/ImageProcessorConsole/images/output/rotate.jpg b/src/ImageProcessorConsole/images/output/rotate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..45c192cd422f3cd55f26d6228d15f75ffc467851 GIT binary patch literal 26227 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 literal 0 HcmV?d00001 From 238374ceeae5335917d589b4c315bc36bd241ef1 Mon Sep 17 00:00:00 2001 From: Jeavon Leopold Date: Mon, 28 Jul 2014 17:40:38 +0100 Subject: [PATCH 2/7] Fix for #70 - Height and width ratio should round up to nearest pixel Former-commit-id: 9d7ccd05efaed9b5d4b15196d8062672b26a2d6e --- src/ImageProcessor.Web/NET45/Processors/Resize.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageProcessor.Web/NET45/Processors/Resize.cs b/src/ImageProcessor.Web/NET45/Processors/Resize.cs index 09ce9bc3c..3c1772911 100644 --- a/src/ImageProcessor.Web/NET45/Processors/Resize.cs +++ b/src/ImageProcessor.Web/NET45/Processors/Resize.cs @@ -205,13 +205,13 @@ namespace ImageProcessor.Web.Processors // Replace 0 width if (size.Width == 0 && size.Height > 0 && input.Contains(WidthRatio) && !input.Contains(HeightRatio)) { - size.Width = (int)(value.ToPositiveFloatArray()[0] * size.Height); + size.Width = (int)Math.Ceiling(value.ToPositiveFloatArray()[0] * size.Height); } // Replace 0 height if (size.Height == 0 && size.Width > 0 && input.Contains(HeightRatio) && !input.Contains(WidthRatio)) { - size.Height = (int)(value.ToPositiveFloatArray()[0] * size.Width); + size.Height = (int)Math.Ceiling(value.ToPositiveFloatArray()[0] * size.Width); } } From 577ffa581389615ef5e7a49baf5533a830241243 Mon Sep 17 00:00:00 2001 From: James South Date: Mon, 4 Aug 2014 15:51:44 +0100 Subject: [PATCH 3/7] Adding AsyncDeDuper Former-commit-id: a5e1f827c161642d6de2b164dcd3224195975ac9 --- .../NET4/ImageProcessor.Web_NET4.csproj | 3 + .../NET45/Helpers/AsyncDeDuperLock.cs | 134 ++++++++++++++++++ .../HttpModules/ImageProcessingModule.cs | 40 +----- .../NET45/ImageProcessor.Web_NET45.csproj | 1 + 4 files changed, 144 insertions(+), 34 deletions(-) create mode 100644 src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs diff --git a/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj b/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj index 82d3986a7..601953a5e 100644 --- a/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj +++ b/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj @@ -101,6 +101,9 @@ StringExtensions.cs + + AsyncDeDuperLock.cs + CommonParameterParserUtility.cs diff --git a/src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs b/src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs new file mode 100644 index 000000000..7d6fbf0cd --- /dev/null +++ b/src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs @@ -0,0 +1,134 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Throttles duplicate requests. +// +// -------------------------------------------------------------------------------------------------------------------- +namespace ImageProcessor.Web.Helpers +{ + using System; + using System.Collections.Concurrent; + using System.Threading; + using System.Threading.Tasks; + + /// + /// Throttles duplicate requests. + /// Based loosely on + /// + public sealed class AsyncDeDuperLock + { + /// + /// The semaphore slims. + /// + private static readonly ConcurrentDictionary SemaphoreSlims = new ConcurrentDictionary(); + + /// + /// The lock. + /// + /// + /// The hash. + /// + /// + /// The . + /// + public IDisposable Lock(string key) + { + DisposableScope releaser = new DisposableScope( + key, + s => + { + SemaphoreSlim locker; + if (SemaphoreSlims.TryRemove(s, out locker)) + { + locker.Release(); + locker.Dispose(); + } + }); + + SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(key, new SemaphoreSlim(1, 1)); + semaphore.Wait(); + return releaser; + } + +#if NET45 && !__MonoCS__ + /// + /// The lock async. + /// + /// + /// The key. + /// + /// + /// The . + /// + public Task LockAsync(string key) + { + DisposableScope releaser = new DisposableScope( + key, + s => + { + SemaphoreSlim locker; + if (SemaphoreSlims.TryRemove(s, out locker)) + { + locker.Release(); + locker.Dispose(); + } + }); + + Task releaserTask = Task.FromResult(releaser as IDisposable); + SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(key, new SemaphoreSlim(1, 1)); + + Task waitTask = semaphore.WaitAsync(); + + return waitTask.IsCompleted + ? releaserTask + : waitTask.ContinueWith( + (_, r) => (IDisposable)r, + releaser, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } +#endif + /// + /// The disposable scope. + /// + internal sealed class DisposableScope : IDisposable + { + /// + /// The key + /// + private readonly string key; + + /// + /// The close scope action. + /// + private readonly Action closeScopeAction; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The key. + /// + /// + /// The close scope action. + /// + public DisposableScope(string key, Action closeScopeAction) + { + this.key = key; + this.closeScopeAction = closeScopeAction; + } + + /// + /// The dispose. + /// + public void Dispose() + { + this.closeScopeAction(this.key); + } + } + } +} \ No newline at end of file diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs index 0f4db86f3..2fe0a275d 100644 --- a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs +++ b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs @@ -12,7 +12,6 @@ namespace ImageProcessor.Web.HttpModules { #region Using using System; - using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -22,7 +21,6 @@ namespace ImageProcessor.Web.HttpModules using System.Security.Permissions; using System.Security.Principal; using System.Text.RegularExpressions; - using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Hosting; @@ -56,9 +54,9 @@ namespace ImageProcessor.Web.HttpModules private static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); /// - /// The collection of SemaphoreSlims for identifying given locking individual queries. + /// The locker for preventing duplicate requests. /// - private static readonly ConcurrentDictionary SemaphoreSlims = new ConcurrentDictionary(); + private static readonly AsyncDeDuperLock Locker = new AsyncDeDuperLock(); /// /// The value to prefix any remote image requests with to ensure they get captured. @@ -149,20 +147,6 @@ namespace ImageProcessor.Web.HttpModules GC.SuppressFinalize(this); } - /// - /// Gets the specific for the given id. - /// - /// - /// The id representing the . - /// - /// - /// The for the given id. - /// - private static SemaphoreSlim GetSemaphoreSlim(string id) - { - return SemaphoreSlims.GetOrAdd(id, new SemaphoreSlim(1, 1)); - } - /// /// Disposes the object and frees resources for the Garbage Collector. /// @@ -377,13 +361,11 @@ namespace ImageProcessor.Web.HttpModules { if (isRemote) { - SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath); #if NET45 && !__MonoCS__ - await semaphore.WaitAsync(); + using (await Locker.LockAsync(cachedPath)) #else - semaphore.Wait(); + using (Locker.Lock(cachedPath)) #endif - try { Uri uri = new Uri(requestPath + "?" + urlParameters); RemoteFile remoteFile = new RemoteFile(uri, false); @@ -422,20 +404,14 @@ namespace ImageProcessor.Web.HttpModules } } } - finally - { - semaphore.Release(); - } } else { - SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath); #if NET45 && !__MonoCS__ - await semaphore.WaitAsync(); + using (await Locker.LockAsync(cachedPath)) #else - semaphore.Wait(); + using (Locker.Lock(cachedPath)) #endif - try { // Check to see if the file exists. // ReSharper disable once AssignNullToNotNullAttribute @@ -460,10 +436,6 @@ namespace ImageProcessor.Web.HttpModules // Trim the cache. await cache.TrimCachedFolderAsync(cachedPath); } - finally - { - semaphore.Release(); - } } } } diff --git a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj index 40bd16bc7..6d4694400 100644 --- a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj +++ b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj @@ -56,6 +56,7 @@ + From ac6921b4b6c5139d69f42e4d0f7b490cbaae49fb Mon Sep 17 00:00:00 2001 From: James South Date: Mon, 4 Aug 2014 20:40:41 +0100 Subject: [PATCH 4/7] Make DisposalScope private Former-commit-id: b0d20c38ada344b0d457db7b78a7680366b3e696 --- src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs b/src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs index 7d6fbf0cd..c6896c3a0 100644 --- a/src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs +++ b/src/ImageProcessor.Web/NET45/Helpers/AsyncDeDuperLock.cs @@ -23,7 +23,8 @@ namespace ImageProcessor.Web.Helpers /// /// The semaphore slims. /// - private static readonly ConcurrentDictionary SemaphoreSlims = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary SemaphoreSlims + = new ConcurrentDictionary(); /// /// The lock. @@ -79,7 +80,7 @@ namespace ImageProcessor.Web.Helpers Task releaserTask = Task.FromResult(releaser as IDisposable); SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(key, new SemaphoreSlim(1, 1)); - + Task waitTask = semaphore.WaitAsync(); return waitTask.IsCompleted @@ -95,7 +96,7 @@ namespace ImageProcessor.Web.Helpers /// /// The disposable scope. /// - internal sealed class DisposableScope : IDisposable + private sealed class DisposableScope : IDisposable { /// /// The key From c068dd012423b8f1d451f202abc1aef6f4504069 Mon Sep 17 00:00:00 2001 From: James South Date: Tue, 5 Aug 2014 00:28:03 +0100 Subject: [PATCH 5/7] WebP is now a separate plugin Former-commit-id: 072872be821a4ac73038dec5a99de6ae2552a713 --- .../HttpModules/ImageProcessingModule.cs | 41 +++++-- src/ImageProcessor.sln | 17 +++ .../ImageProcessorBootstrapper.cs | 85 ++++++++++++++- src/ImageProcessor/ImageProcessor.csproj | 6 -- .../ImageProcessorConsole.csproj | 4 + .../images/output/4.sm.webp | Bin 0 -> 12162 bytes .../images/output/test.webp | Bin 0 -> 5590 bytes .../ImageProcessor.Plugins.WebP.csproj | 67 ++++++++++++ .../Imaging/Formats/NativeMethods.cs | 6 +- .../Imaging/Formats/WebPFormat.cs | 3 +- .../Properties/AssemblyInfo.cs | 36 +++++++ .../Unmanaged/x64/libwebp.dll.REMOVED.git-id | 0 .../Unmanaged/x86/libwebp.dll.REMOVED.git-id | 0 .../Controllers/HomeController.cs | 5 + .../Test_Website_MVC_NET45.csproj | 7 ++ .../Test_Website_NET45/Views/Home/WebP.cshtml | 100 ++++++++++++++++++ .../Views/Shared/_Layout.cshtml | 1 + 17 files changed, 355 insertions(+), 23 deletions(-) create mode 100644 src/ImageProcessorConsole/images/output/4.sm.webp create mode 100644 src/ImageProcessorConsole/images/output/test.webp create mode 100644 src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/ImageProcessor.Plugins.WebP.csproj rename src/{ImageProcessor => Plugins/ImageProcessor/ImageProcessor.Plugins.WebP}/Imaging/Formats/NativeMethods.cs (96%) rename src/{ImageProcessor => Plugins/ImageProcessor/ImageProcessor.Plugins.WebP}/Imaging/Formats/WebPFormat.cs (98%) create mode 100644 src/Plugins/ImageProcessor/ImageProcessor.Plugins.WebP/Properties/AssemblyInfo.cs rename src/{ImageProcessor => Plugins/ImageProcessor/ImageProcessor.Plugins.WebP}/Resources/Unmanaged/x64/libwebp.dll.REMOVED.git-id (100%) rename src/{ImageProcessor => Plugins/ImageProcessor/ImageProcessor.Plugins.WebP}/Resources/Unmanaged/x86/libwebp.dll.REMOVED.git-id (100%) create mode 100644 src/TestWebsites/NET45/Test_Website_NET45/Views/Home/WebP.cshtml 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.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..59f975fb3 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,42 @@ namespace ImageProcessor.Configuration try { Type type = typeof(ISupportedImageFormat); + HashSet found = new HashSet(); + + // Get any references and used assemblies. + foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + this.LoadReferencedAssemblies(found, assembly); + } + + // 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); + + 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 +116,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")
  • From 98b68b1ac8c38d41acc31af3976b71d66a0a42f1 Mon Sep 17 00:00:00 2001 From: James South Date: Tue, 5 Aug 2014 13:32:14 +0100 Subject: [PATCH 6/7] Fix dll load plus regex tests Former-commit-id: 2778a682ce325c1739e49f1e88ab7c5a8fa9f431 --- .../Extensions/DoubleExtensionsUnitTests.cs | 36 +--- .../ImageProcessor.Web.UnitTests.csproj | 4 + .../RegularExpressionUnitTests.cs | 201 +++++++++++++----- .../Helpers/CommonParameterParserUtility.cs | 2 +- .../ImageProcessorBootstrapper.cs | 8 +- 5 files changed, 161 insertions(+), 90 deletions(-) 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..86c346e38 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; /// @@ -40,37 +44,55 @@ namespace ImageProcessor.Web.UnitTests } /// - /// 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); + brightness.MatchRegexIndex(input); + int result = brightness.Processor.DynamicParameter; - int actual = 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); - - int actual = contrast.Processor.DynamicParameter; + contrast.MatchRegexIndex(input); + int result = contrast.Processor.DynamicParameter; - Assert.AreEqual(Expected, actual); + Assert.AreEqual(expected, result); } /// @@ -93,52 +115,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); - - ISupportedImageFormat actual = format.Processor.DynamicParameter; + format.MatchRegexIndex(input); + Type result = format.Processor.DynamicParameter.GetType(); - 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); - - int actual = quality.Processor.DynamicParameter; + quality.MatchRegexIndex(input); + int result = quality.Processor.DynamicParameter; - Assert.AreEqual(Expected, actual); + Assert.AreEqual(expected, result); } /// @@ -161,18 +233,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 +260,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); + } } /// diff --git a/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs b/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs index 505d80d99..22c266a85 100644 --- a/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs +++ b/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs @@ -37,7 +37,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. diff --git a/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs b/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs index 59f975fb3..4cffe51eb 100644 --- a/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs +++ b/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs @@ -73,13 +73,6 @@ namespace ImageProcessor.Configuration try { Type type = typeof(ISupportedImageFormat); - HashSet found = new HashSet(); - - // Get any references and used assemblies. - foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - this.LoadReferencedAssemblies(found, assembly); - } // Get any referenced but not used assemblies. Assembly executingAssembly = Assembly.GetExecutingAssembly(); @@ -88,6 +81,7 @@ namespace ImageProcessor.Configuration // 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); From 72a3e8eed8b78351bdf607c6c453158e30622cb7 Mon Sep 17 00:00:00 2001 From: James South Date: Tue, 5 Aug 2014 14:43:07 +0100 Subject: [PATCH 7/7] Moar tests plus known colours! Former-commit-id: 38f02083c8cae1bf549c0863c4842e196a0b90f5 --- .../RegularExpressionUnitTests.cs | 82 ++++++++++++++----- .../Helpers/CommonParameterParserUtility.cs | 42 +++++++++- .../NET45/Processors/Format.cs | 1 - 3 files changed, 103 insertions(+), 22 deletions(-) diff --git a/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs b/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs index 86c346e38..db81f4533 100644 --- a/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs +++ b/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs @@ -29,18 +29,25 @@ 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); - - int actual = alpha.Processor.DynamicParameter; + alpha.MatchRegexIndex(input); + int result = alpha.Processor.DynamicParameter; - Assert.AreEqual(Expected, actual); + Assert.AreEqual(expected, result); } /// @@ -95,6 +102,32 @@ namespace ImageProcessor.Web.UnitTests Assert.AreEqual(expected, result); } + /// + /// 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); + } + /// /// The rotate regex unit test. /// @@ -288,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 22c266a85..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. @@ -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/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;