Browse Source

Merging tiff fix

Former-commit-id: dc1f5a3688ad4e88c61b131159f966f399ff1906
pull/17/head
James South 12 years ago
parent
commit
6174f5293f
  1. 8
      README.md
  2. 54
      build/Build.ImageProcessor.Plugins.WebP.proj
  3. 39
      build/Build.ImageProcessor.Web.proj
  4. 34
      build/Build.ImageProcessor.proj
  5. 14
      build/Build.bat
  6. 32
      build/NuSpecs/ImageProcessor.Plugins.WebP.nuspec
  7. 22
      build/NuSpecs/ImageProcessor.Web.Config.nuspec
  8. 16
      build/NuSpecs/ImageProcessor.Web.nuspec
  9. 9
      build/NuSpecs/ImageProcessor.nuspec
  10. 9
      build/content/ImageProcessor.Plugins.WebP/web.config.transform
  11. 6
      build/content/ImageProcessor.Web.Config/web.config.transform
  12. 4
      build/content/ImageProcessor.Web/web.config.transform
  13. 38
      src/ImageProcessor.UnitTests/Extensions/DoubleExtensionsUnitTests.cs
  14. 37
      src/ImageProcessor.UnitTests/Extensions/IntegerExtensionsUnitTests.cs
  15. 564
      src/ImageProcessor.UnitTests/ImageFactoryUnitTests.cs
  16. 88
      src/ImageProcessor.UnitTests/ImageProcessor.UnitTests.csproj
  17. 1
      src/ImageProcessor.UnitTests/Images/autorotate.jpg.REMOVED.git-id
  18. 1
      src/ImageProcessor.UnitTests/Images/cmyk-profile-euroscale.jpg.REMOVED.git-id
  19. 1
      src/ImageProcessor.UnitTests/Images/cmyk.jpg.REMOVED.git-id
  20. 1
      src/ImageProcessor.UnitTests/Images/color-vision-test.gif.REMOVED.git-id
  21. 1
      src/ImageProcessor.UnitTests/Images/exif-Tulips.jpg.REMOVED.git-id
  22. 1
      src/ImageProcessor.UnitTests/Images/exif-rocks.jpg.REMOVED.git-id
  23. 1
      src/ImageProcessor.UnitTests/Images/format-Penguins-8bit.png.REMOVED.git-id
  24. 1
      src/ImageProcessor.UnitTests/Images/format-Penguins.bmp.REMOVED.git-id
  25. 1
      src/ImageProcessor.UnitTests/Images/format-Penguins.gif.REMOVED.git-id
  26. 1
      src/ImageProcessor.UnitTests/Images/format-Penguins.jpg.REMOVED.git-id
  27. 1
      src/ImageProcessor.UnitTests/Images/format-Penguins.png.REMOVED.git-id
  28. 1
      src/ImageProcessor.UnitTests/Images/format-Penguins.tif.REMOVED.git-id
  29. 1
      src/ImageProcessor.UnitTests/Images/format-animated.gif.REMOVED.git-id
  30. BIN
      src/ImageProcessor.UnitTests/Images/hi-color.png
  31. BIN
      src/ImageProcessor.UnitTests/Images/hi-contrast.jpg
  32. BIN
      src/ImageProcessor.UnitTests/Images/hi-saturation.jpg
  33. 1
      src/ImageProcessor.UnitTests/Images/profile-adobe-rgb.jpg.REMOVED.git-id
  34. 1
      src/ImageProcessor.UnitTests/Images/profile-srgb.jpg.REMOVED.git-id
  35. BIN
      src/ImageProcessor.UnitTests/Images/size-Penguins-200.jpg
  36. BIN
      src/ImageProcessor.UnitTests/Images/text-over-transparent.png
  37. BIN
      src/ImageProcessor.UnitTests/Images/udendørs.jpg
  38. 9
      src/ImageProcessor.UnitTests/Properties/AssemblyInfo.cs
  39. 217
      src/ImageProcessor.UnitTests/RegularExpressionUnitTests.cs
  40. 2
      src/ImageProcessor.UnitTests/packages.config
  41. 160
      src/ImageProcessor.Web.UnitTests/Extensions/StringExtensionsUnitTests.cs
  42. 112
      src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj
  43. 11
      src/ImageProcessor.Web.UnitTests/Properties/AssemblyInfo.cs
  44. 474
      src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs
  45. 4
      src/ImageProcessor.Web.UnitTests/packages.config
  46. 55
      src/ImageProcessor.Web/Caching/CacheIndexer.cs
  47. 2
      src/ImageProcessor.Web/Caching/CachedImage.cs
  48. 226
      src/ImageProcessor.Web/Caching/DiskCache.cs
  49. 0
      src/ImageProcessor.Web/Caching/MemCache.cs
  50. 9
      src/ImageProcessor.Web/Configuration/ImageCacheSection.cs
  51. 8
      src/ImageProcessor.Web/Configuration/ImageProcessingSection.cs
  52. 54
      src/ImageProcessor.Web/Configuration/ImageProcessorConfiguration.cs
  53. 4
      src/ImageProcessor.Web/Configuration/ImageSecuritySection.cs
  54. 1
      src/ImageProcessor.Web/Configuration/Resources/cache.config
  55. 43
      src/ImageProcessor.Web/Configuration/Resources/processing.config
  56. 0
      src/ImageProcessor.Web/Configuration/Resources/security.config
  57. 2
      src/ImageProcessor.Web/Extensions/DirectoryInfoExtensions.cs
  58. 48
      src/ImageProcessor.Web/Extensions/StringExtensions.cs
  59. 134
      src/ImageProcessor.Web/Helpers/AsyncDuplicateLock.cs
  60. 268
      src/ImageProcessor.Web/Helpers/CommonParameterParserUtility.cs
  61. 133
      src/ImageProcessor.Web/Helpers/ImageHelpers.cs
  62. 44
      src/ImageProcessor.Web/Helpers/NativeMethods.cs
  63. 25
      src/ImageProcessor.Web/Helpers/PostProcessingEventArgs.cs
  64. 61
      src/ImageProcessor.Web/Helpers/RemoteFile.cs
  65. 0
      src/ImageProcessor.Web/Helpers/ResourceHelpers.cs
  66. 269
      src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs
  67. 66
      src/ImageProcessor.Web/ImageFactoryExtensions.cs
  68. 43
      src/ImageProcessor.Web/ImageProcessor.Web.csproj
  69. 146
      src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj
  70. 11
      src/ImageProcessor.Web/NET4/Settings.StyleCop
  71. 15
      src/ImageProcessor.Web/NET4/app.config
  72. 6
      src/ImageProcessor.Web/NET4/packages.config
  73. 1
      src/ImageProcessor.Web/NET45/Config/Resources/cache.config
  74. 41
      src/ImageProcessor.Web/NET45/Config/Resources/processing.config
  75. 57
      src/ImageProcessor.Web/NET45/Helpers/ImageHelpers.cs
  76. 51
      src/ImageProcessor.Web/NET45/Helpers/TaskHelpers.cs
  77. 130
      src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs
  78. 18
      src/ImageProcessor.Web/NET45/Settings.StyleCop
  79. 90
      src/ImageProcessor.Web/Processors/Alpha.cs
  80. 94
      src/ImageProcessor.Web/Processors/AutoRotate.cs
  81. 88
      src/ImageProcessor.Web/Processors/BackgroundColor.cs
  82. 89
      src/ImageProcessor.Web/Processors/Brightness.cs
  83. 89
      src/ImageProcessor.Web/Processors/Contrast.cs
  84. 167
      src/ImageProcessor.Web/Processors/Crop.cs
  85. 161
      src/ImageProcessor.Web/Processors/Filter.cs
  86. 103
      src/ImageProcessor.Web/Processors/Flip.cs
  87. 166
      src/ImageProcessor.Web/Processors/Format.cs
  88. 101
      src/ImageProcessor.Web/Processors/GaussianBlur.cs
  89. 101
      src/ImageProcessor.Web/Processors/GaussianSharpen.cs
  90. 51
      src/ImageProcessor.Web/Processors/IWebGraphicsProcessor.cs
  91. 92
      src/ImageProcessor.Web/Processors/Meta.cs
  92. 90
      src/ImageProcessor.Web/Processors/Quality.cs
  93. 325
      src/ImageProcessor.Web/Processors/Resize.cs
  94. 96
      src/ImageProcessor.Web/Processors/Rotate.cs
  95. 173
      src/ImageProcessor.Web/Processors/RoundedCorners.cs
  96. 92
      src/ImageProcessor.Web/Processors/Saturation.cs
  97. 85
      src/ImageProcessor.Web/Processors/Tint.cs
  98. 98
      src/ImageProcessor.Web/Processors/Vignette.cs
  99. 341
      src/ImageProcessor.Web/Processors/Watermark.cs
  100. 31
      src/ImageProcessor.Web/Properties/AssemblyInfo.cs

8
README.md

@ -1,7 +1,9 @@
ImageProcessor
===============
Imageprocessor is a lightweight library written in C# that allows you to manipulate images on-the-fly using .NET 4.0.
[![Build status](https://ci.appveyor.com/api/projects/status/8ypr7527dnao04yr)](https://ci.appveyor.com/project/JamesSouth/imageprocessor)
Imageprocessor is a lightweight library written in C# that allows you to manipulate images on-the-fly using .NET 4.5+
It's fast, extensible, easy to use, comes bundled with some great features and is fully open source.
@ -26,8 +28,8 @@ I want the next version of ImageProcessor to run on all devices. Sadly it looks
ImageProcessor's documentation, included in this repo in the gh_pages directory, is built with [Jekyll](http://jekyllrb.com) and publicly hosted on GitHub Pages at <http://imageprocessor.org>. The docs may also be run locally.
### Running documentation locally
1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v1.5.x).
1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v2.2.x).
- **Windows users:** Read [this unofficial guide](https://github.com/juthilo/run-jekyll-on-windows/) to get Jekyll up and running without problems.
2. From the root `/ImageProcessor` directory, run `jekyll serve` in the command line.
3. Open <http://localhost:4000> in your browser to navigate to your site.
Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/).
Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/).

54
build/Build.ImageProcessor.Plugins.WebP.proj

@ -0,0 +1,54 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildCommunityTasksPath>.\</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project=".\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!--
****************************************************
VARIABLES
*****************************************************
-->
<PropertyGroup>
<BuildConfiguration>Release</BuildConfiguration>
<BuildFolder>_BuildOutput\</BuildFolder>
<IncludeSymbols>False</IncludeSymbols>
<BuildFolderAbsolutePath>$(MSBuildProjectDirectory)\$(BuildFolder)</BuildFolderAbsolutePath>
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)ImageProcessor.Plugins.WebP\lib\net45</SolutionBinFolderAbsolutePath>
<BuildInputDir>..\src\Plugins\ImageProcessor\ImageProcessor.Plugins.WebP\</BuildInputDir>
</PropertyGroup>
<!--
****************************************************
TARGETS
*****************************************************
-->
<Target Name="Build" DependsOnTargets="BuildImageProcessorPluginsWebP">
<Message Text="Build finished" />
</Target>
<Target Name="BuildImageProcessorPluginsWebP" DependsOnTargets="SetVersionNumber">
<Message Text="Compiling ImageProcessor.Plugins.WebP project to build\$(BuildFolder)" Importance="High" />
<MSBuild Projects="$(BuildInputDir)\ImageProcessor.Plugins.WebP.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
<Message Text="Finished compiling project" Importance="High" />
</Target>
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
<Message Text="Creating Version File: $(BUILD_RELEASE)"/>
<ItemGroup>
<AssemblyFiles Include="$(BuildInputDir)Properties\AssemblyInfo.cs;" />
</ItemGroup>
<FileUpdate Files="@(AssemblyFiles)"
Multiline="true"
Singleline="false"
Regex="(AssemblyVersion|AssemblyFileVersionAttribute|AssemblyFileVersion)\(&quot;([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?&quot;\)"
ReplacementText="$1(&quot;$(BUILD_RELEASE)&quot;)" />
</Target>
</Project>

39
build/Build.ImageProcessor.Web.proj

@ -2,15 +2,15 @@
<PropertyGroup>
<MSBuildCommunityTasksPath>.\</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project=".\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!--
<Import Project=".\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!--
****************************************************
VARIABLES
VARIABLES
*****************************************************
-->
<PropertyGroup>
<BuildConfiguration>Release</BuildConfiguration>
<BuildFolder>_BuildOutput\</BuildFolder>
@ -19,42 +19,39 @@
<BuildFolderAbsolutePath>$(MSBuildProjectDirectory)\$(BuildFolder)</BuildFolderAbsolutePath>
<SolutionBinFolder>$(BuildFolder)bin\</SolutionBinFolder>
<SolutionBinFolderRelativeToProjects>$(BuildFolderRelativeToProjects)bin\</SolutionBinFolderRelativeToProjects>
<SolutionBinFolderAbsolutePath4>$(BuildFolderAbsolutePath)ImageProcessor.Web\lib\net40</SolutionBinFolderAbsolutePath4>
<SolutionBinFolderAbsolutePath45>$(BuildFolderAbsolutePath)ImageProcessor.Web\lib\net45</SolutionBinFolderAbsolutePath45>
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)ImageProcessor.Web\lib\net45</SolutionBinFolderAbsolutePath>
<BuildInputDir>..\src\ImageProcessor.Web\</BuildInputDir>
</PropertyGroup>
<!--
<!--
****************************************************
TARGETS
TARGETS
*****************************************************
-->
<Target Name="Build" DependsOnTargets="BuildImageProcessorWeb">
<Message Text="Build finished" />
</Target>
<Target Name="BuildImageProcessorWeb" DependsOnTargets="SetVersionNumber">
<Message Text="Compiling ImageProcessor.Web projects to build\$(BuildFolder)" Importance="High" />
<MSBuild Projects="$(BuildInputDir)NET4\ImageProcessor.Web_NET4.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath4);" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
<MSBuild Projects="$(BuildInputDir)NET45\ImageProcessor.Web_NET45.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath45);" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
<MSBuild Projects="$(BuildInputDir)\ImageProcessor.Web.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
<Message Text="Finished compiling projects" Importance="High" />
</Target>
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
<Message Text="Creating Version File: $(BUILD_RELEASE)"/>
<ItemGroup>
<AssemblyFiles Include="$(BuildInputDir)NET45\Properties\AssemblyInfo.cs;" />
</ItemGroup>
<AssemblyFiles Include="$(BuildInputDir)Properties\AssemblyInfo.cs;" />
</ItemGroup>
<FileUpdate Files="@(AssemblyFiles)"
Multiline="true"
Singleline="false"
Regex="(AssemblyVersion|AssemblyFileVersionAttribute|AssemblyFileVersion)\(&quot;([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?&quot;\)"
ReplacementText="$1(&quot;$(BUILD_RELEASE)&quot;)" />
</Target>
</Project>
</Project>

34
build/Build.ImageProcessor.proj

@ -2,53 +2,53 @@
<PropertyGroup>
<MSBuildCommunityTasksPath>.\</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project=".\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!--
<Import Project=".\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!--
****************************************************
VARIABLES
VARIABLES
*****************************************************
-->
<PropertyGroup>
<BuildConfiguration>Release</BuildConfiguration>
<BuildFolder>_BuildOutput\</BuildFolder>
<IncludeSymbols>False</IncludeSymbols>
<BuildFolderAbsolutePath>$(MSBuildProjectDirectory)\$(BuildFolder)</BuildFolderAbsolutePath>
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)ImageProcessor\lib\</SolutionBinFolderAbsolutePath>
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)ImageProcessor\lib\net45</SolutionBinFolderAbsolutePath>
<BuildInputDir>..\src\ImageProcessor\</BuildInputDir>
</PropertyGroup>
<!--
<!--
****************************************************
TARGETS
TARGETS
*****************************************************
-->
<Target Name="Build" DependsOnTargets="BuildImageProcessor">
<Message Text="Build finished" />
</Target>
<Target Name="BuildImageProcessor" DependsOnTargets="SetVersionNumber">
<Message Text="Compiling ImageProcessor project to build\$(BuildFolder)" Importance="High" />
<MSBuild Projects="$(BuildInputDir)\ImageProcessor.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
<Message Text="Finished compiling project" Importance="High" />
</Target>
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
<Message Text="Creating Version File: $(BUILD_RELEASE)"/>
<ItemGroup>
<AssemblyFiles Include="$(BuildInputDir)Properties\AssemblyInfo.cs;" />
</ItemGroup>
</ItemGroup>
<FileUpdate Files="@(AssemblyFiles)"
Multiline="true"
Singleline="false"
Regex="(AssemblyVersion|AssemblyFileVersionAttribute|AssemblyFileVersion)\(&quot;([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?&quot;\)"
ReplacementText="$1(&quot;$(BUILD_RELEASE)&quot;)" />
</Target>
</Project>
</Project>

14
build/Build.bat

@ -1,24 +1,24 @@
@ECHO OFF
SET version=1.9.5.0
SET webversion=3.3.0.0
SET webconfigversion=1.1.3.0
ECHO Building ImageProcessor %version%, ImageProcess.Web %webversion% and ImageProcess.Web.Config %webconfigversion%
SET version=2.0.0.0
SET webversion=4.0.0.0
SET webconfigversion=2.0.0.0
SET webppluginversion=1.0.0.0
ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise you'd have to run build.cmd twice
SET nuGetFolder=%CD%\..\src\packages\
..\src\.nuget\NuGet.exe install ..\src\ImageProcessor.Web\NET4\packages.config -OutputDirectory %nuGetFolder%
ECHO Building ImageProcessor %version%, ImageProcessor.Web %webversion%, ImageProcessor.Web.Config %webconfigversion%, and ImageProcessor.Plugins.WebP %webppluginversion%
ECHO Removing _BuildOutput directory so everything is nice and clean
RD _BuildOutput /q /s
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.ImageProcessor.proj" /p:BUILD_RELEASE=%version% /p:BUILD_COMMENT=%comment%
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.ImageProcessor.Web.proj" /p:BUILD_RELEASE=%webversion% /p:BUILD_COMMENT=%comment%
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.ImageProcessor.Plugins.WebP.proj" /p:BUILD_RELEASE=%webppluginversion% /p:BUILD_COMMENT=%comment%
ECHO Packing the NuGet release files
..\src\.nuget\NuGet.exe pack NuSpecs\ImageProcessor.nuspec -Version %version%
..\src\.nuget\NuGet.exe pack NuSpecs\ImageProcessor.Web.nuspec -Version %webversion%
..\src\.nuget\NuGet.exe pack NuSpecs\ImageProcessor.Web.Config.nuspec -Version %webconfigversion%
..\src\.nuget\NuGet.exe pack NuSpecs\ImageProcessor.Plugins.WebP.nuspec -Version %webppluginversion%
PAUSE
IF ERRORLEVEL 1 GOTO :showerror

32
build/NuSpecs/ImageProcessor.Plugins.WebP.nuspec

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>ImageProcessor.Plugins.WebP</id>
<version>1.0.0.0</version>
<title>ImageProcessor.Plugins.WebP</title>
<authors>James South</authors>
<owners>James South</owners>
<projectUrl>http://imageprocessor.org</projectUrl>
<iconUrl>http://raw.githubusercontent.com/JimBobSquarePants/ImageProcessor/master/build/content/imageprocessor.128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Adds support to ImageProcessor for the WebP image format.
If you use ImageProcessor please get in touch via my twitter @james_m_south
Feedback is always welcome</description>
<summary>Adds support to ImageProcessor for the WebP image format.</summary>
<releaseNotes />
<copyright>James South</copyright>
<language>en-GB</language>
<tags>Image Imaging ASP Performance Processing HttpModule Cache Resize Rotate RoundedCorners Flip Crop Filter Effects Quality Watermark Alpha Vignette Saturation Brightness Contrast Gif Jpg Jpeg Bitmap Png WebP Fluent GDI Gaussian Blur Sharpen Tint Quantizer Animated</tags>
<dependencies>
<group targetFramework=".NETFramework4.5">
<dependency id="ImageProcessor" version="2.0.0.0" />
</group>
</dependencies>
</metadata>
<files>
<file src="..\_BuildOutput\ImageProcessor.Plugins.WebP\lib\net45\ImageProcessor.Plugins.WebP.dll" target="lib\net45\ImageProcessor.Plugins.WebP.dll" />
<file src="..\content\ImageProcessor.Plugins.WebP\web.config.transform" target="content\web.config.transform" />
</files>
</package>

22
build/NuSpecs/ImageProcessor.Web.Config.nuspec

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>ImageProcessor.Web.Config</id>
@ -14,26 +14,22 @@
If you use ImageProcessor please get in touch via my twitter @james_m_south
Feedback is always welcome</description>
<summary>ImageProcessor.Web configuration settings for ASP.NET websites.</summary>
<summary>ImageProcessor.Web configuration settings for ASP.NET websites.</summary>
<releaseNotes />
<copyright>James South</copyright>
<language>en-GB</language>
<tags>Image Imaging ASP Performance Processing HttpModule Cache Resize Rotate RoundedCorners Flip Crop Filter Effects Quality Watermark Alpha Vignette Saturation Brightness Contrast Gif Jpg Jpeg Bitmap Png Fluent GDI Gaussian Blur Sharpen Tint Quantizer Animated</tags>
<tags>Image Imaging ASP Performance Processing HttpModule Cache Resize Rotate RoundedCorners Flip Crop Filter Effects Quality Watermark Alpha Vignette Saturation Brightness Contrast Gif Jpg Jpeg Bitmap Png WebP Fluent GDI Gaussian Blur Sharpen Tint Quantizer Animated</tags>
<dependencies>
<group targetFramework=".NETFramework4.0">
<dependency id="ImageProcessor" version="1.9.4.0" />
<dependency id="ImageProcessor.Web" version="3.2.9.0" />
</group>
<group targetFramework=".NETFramework4.5">
<dependency id="ImageProcessor" version="1.9.4.0" />
<dependency id="ImageProcessor.Web" version="3.2.9.0" />
<dependency id="ImageProcessor" version="2.0.0.0" />
<dependency id="ImageProcessor.Web" version="4.0.0.0" />
</group>
</dependencies>
</metadata>
<files>
<file src="..\..\src\ImageProcessor.Web\NET45\Config\Resources\cache.config" target="content\config\imageprocessor\cache.config" />
<file src="..\..\src\ImageProcessor.Web\NET45\Config\Resources\processing.config" target="content\config\imageprocessor\processing.config" />
<file src="..\..\src\ImageProcessor.Web\NET45\Config\Resources\security.config" target="content\config\imageprocessor\security.config" />
<file src="..\..\src\ImageProcessor.Web\Configuration\Resources\cache.config" target="content\config\imageprocessor\cache.config" />
<file src="..\..\src\ImageProcessor.Web\Configuration\Resources\processing.config" target="content\config\imageprocessor\processing.config" />
<file src="..\..\src\ImageProcessor.Web\Configuration\Resources\security.config" target="content\config\imageprocessor\security.config" />
<file src="..\content\ImageProcessor.Web.Config\web.config.transform" target="content\web.config.transform" />
</files>
</package>
</package>

16
build/NuSpecs/ImageProcessor.Web.nuspec

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>ImageProcessor.Web</id>
@ -13,8 +13,6 @@
Methods include: Resize, Rotate, Rounded Corners, Flip, Crop, Watermark, Filter, Saturation, Brightness, Contrast, Quality, Format, Vignette, Gaussian Blur, Gaussian Sharpen, and Transparency.
This package also requires Microsoft.Bcl.Async on .NET 4.0 which will be added on install if applicable.
If you use ImageProcessor please get in touch via my twitter @james_m_south
Feedback is always welcome</description>
@ -22,21 +20,15 @@ Feedback is always welcome</description>
<releaseNotes />
<copyright>James South</copyright>
<language>en-GB</language>
<tags>Image Imaging ASP Performance Processing HttpModule Cache Resize AutoRotate Rotate RoundedCorners Flip Crop Filter Effects Quality Watermark Alpha Vignette Saturation Brightness Contrast Gif Jpg Jpeg Bitmap Png Fluent GDI Gaussian Blur Sharpen Tint Quantizer Animated EXIF</tags>
<tags>Image Imaging ASP Performance Processing HttpModule Cache Resize AutoRotate Rotate RoundedCorners Flip Crop Filter Effects Quality Watermark Alpha Vignette Saturation Brightness Contrast Gif Jpg Jpeg Bitmap Png WebP Fluent GDI Gaussian Blur Sharpen Tint Quantizer Animated EXIF</tags>
<dependencies>
<group targetFramework=".NETFramework4.0">
<dependency id="Microsoft.Bcl.Async" version="1.0.168" />
<dependency id="Microsoft.Bcl" version="1.1.9" />
<dependency id="ImageProcessor" version="1.9.4.0" />
</group>
<group targetFramework=".NETFramework4.5">
<dependency id="ImageProcessor" version="1.9.4.0" />
<dependency id="ImageProcessor" version="2.0.0.0" />
</group>
</dependencies>
</metadata>
<files>
<file src="..\content\ImageProcessor.Web\web.config.transform" target="content\web.config.transform" />
<file src="..\_BuildOutput\ImageProcessor.Web\lib\net40\ImageProcessor.Web.dll" target="lib\net40\ImageProcessor.Web.dll" />
<file src="..\_BuildOutput\ImageProcessor.Web\lib\net45\ImageProcessor.Web.dll" target="lib\net45\ImageProcessor.Web.dll" />
</files>
</package>
</package>

9
build/NuSpecs/ImageProcessor.nuspec

@ -9,21 +9,20 @@
<projectUrl>http://imageprocessor.org</projectUrl>
<iconUrl>http://raw.githubusercontent.com/JimBobSquarePants/ImageProcessor/master/build/content/imageprocessor.128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Image Processor is an easy to use and extend processing library written in C#. Its fluent API makes common imaging tasks very simple to perform.
<description>Image Processor is an easy to use and extend processing library written in C#. Its fluent API makes common imaging tasks very simple to perform.
Methods include; Resize, Rotate, Rounded Corners, Flip, Crop, Watermark, Filter, Saturation, Brightness, Contrast, Quality, Format, Vignette, Gaussian Blur, Gaussian Sharpen, and Transparency.
If you use ImageProcessor please get in touch on my twitter @james_m_south.
Feedback is always welcome.</description>
<summary>A library for manipulating image files written in C#.</summary>
<releaseNotes />
<copyright>James South</copyright>
<language>en-GB</language>
<tags>Image Imaging ASP Performance Processing Resize AutoRotate Rotate RoundedCorners Flip Crop Filter Effects Quality Watermark Alpha Vignette Saturation Brightness Contrast Gif Jpg Jpeg Bitmap Png Fluent GDI Gaussian Blur Sharpen Tint Quantizer Animated EXIF</tags>
<tags>Image Imaging ASP Performance Processing Resize AutoRotate Rotate RoundedCorners Flip Crop Filter Effects Quality Watermark Alpha Vignette Saturation Brightness Contrast Gif Jpg Jpeg Bitmap Png WebP Fluent GDI Gaussian Blur Sharpen Tint Quantizer Animated EXIF</tags>
</metadata>
<files>
<file src="..\_BuildOutput\ImageProcessor\lib\ImageProcessor.dll" target="lib\ImageProcessor.dll" />
<file src="..\_BuildOutput\ImageProcessor\lib\net45\ImageProcessor.dll" target="lib\net45\ImageProcessor.dll" />
</files>
</package>
</package>

9
build/content/ImageProcessor.Plugins.WebP/web.config.transform

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<configuration>
<system.webServer>
<staticContent>
<remove fileExtension=".webp"/>
<mimeMap fileExtension=".webp" mimeType="image/webp" />
</staticContent>
</system.webServer>
</configuration>

6
build/content/ImageProcessor.Web.Config/web.config.transform

@ -2,9 +2,9 @@
<configuration>
<configSections>
<sectionGroup name="imageProcessor">
<section name="security" requirePermission="false" type="ImageProcessor.Web.Config.ImageSecuritySection, ImageProcessor.Web" />
<section name="processing" requirePermission="false" type="ImageProcessor.Web.Config.ImageProcessingSection, ImageProcessor.Web" />
<section name="cache" requirePermission="false" type="ImageProcessor.Web.Config.ImageCacheSection, ImageProcessor.Web" />
<section name="security" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageSecuritySection, ImageProcessor.Web"/>
<section name="processing" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageProcessingSection, ImageProcessor.Web"/>
<section name="cache" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageCacheSection, ImageProcessor.Web"/>
</sectionGroup>
</configSections>

4
build/content/ImageProcessor.Web/web.config.transform

@ -7,6 +7,10 @@
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<staticContent>
<remove fileExtension=".webp"/>
<mimeMap fileExtension=".webp" mimeType="image/webp" />
</staticContent>
<modules>
<add name="ImageProcessorModule" type="ImageProcessor.Web.HttpModules.ImageProcessingModule, ImageProcessor.Web"/>
</modules>

38
src/ImageProcessor.UnitTests/Extensions/DoubleExtensionsUnitTests.cs

@ -0,0 +1,38 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DoubleExtensionsUnitTests.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Runs unit tests on the <see cref="DoubleExtensions" /> extension methods
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.UnitTests.Extensions
{
using Common.Extensions;
using NUnit.Framework;
/// <summary>
/// Test harness for the DoubleExtensions extension methods
/// </summary>
[TestFixture]
public class DoubleExtensionsUnitTests
{
/// <summary>
/// Tests the double to byte conversion
/// </summary>
/// <param name="input">Double input</param>
/// <param name="expected">Expected result</param>
[Test]
[TestCase(-10, 0x0)]
[TestCase(1.5, 0x1)]
[TestCase(25.7, 0x19)]
[TestCase(1289047, 0xFF)]
public void TestDoubleToByte(double input, byte expected)
{
byte result = input.ToByte();
Assert.AreEqual(expected, result);
}
}
}

37
src/ImageProcessor.UnitTests/Extensions/IntegerExtensionsUnitTests.cs

@ -0,0 +1,37 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="IntegerExtensionsUnitTests.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Runs unit tests on the <see cref="IntegerExtensions" /> extension methods
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.UnitTests.Extensions
{
using Common.Extensions;
using NUnit.Framework;
/// <summary>
/// Provides a test harness for the integer extension class
/// </summary>
[TestFixture]
public class IntegerExtensionsUnitTests
{
/// <summary>
/// Tests the "ToByte" extension
/// </summary>
/// <param name="input">Integer input</param>
/// <param name="expected">Expected result</param>
[Test]
[TestCase(21, 0x15)]
[TestCase(190, 0xBE)]
[TestCase(3156, 0xFF)]
public void ToByteTest(int input, byte expected)
{
byte result = input.ToByte();
Assert.AreEqual(expected, result);
}
}
}

564
src/ImageProcessor.UnitTests/ImageFactoryUnitTests.cs

@ -11,7 +11,10 @@
namespace ImageProcessor.UnitTests
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using NUnit.Framework;
/// <summary>
@ -21,29 +24,562 @@ namespace ImageProcessor.UnitTests
public class ImageFactoryUnitTests
{
/// <summary>
/// The path to the binary's folder
/// The list of images. Designed to speed up the tests a little.
/// </summary>
private readonly string localPath = Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
private IEnumerable<FileInfo> images;
/// <summary>
/// Tests the loading of image from a file
/// </summary>
[Test]
[TestCase("Chrysanthemum.jpg", "image/jpeg")]
[TestCase("Desert.jpg", "image/jpeg")]
[TestCase("cmyk.png", "image/png")]
[TestCase("Penguins.bmp", "image/bmp")]
[TestCase("Penguins.gif", "image/gif")]
public void TestLoadImageFromFile(string fileName, string expectedMime)
public void TestLoadImageFromFile()
{
var testPhoto = Path.Combine(this.localPath, string.Format("Images/{0}", fileName));
using (ImageFactory imageFactory = new ImageFactory())
foreach (FileInfo file in this.ListInputFiles())
{
imageFactory.Load(testPhoto);
Assert.AreEqual(testPhoto, imageFactory.ImagePath);
Assert.AreEqual(expectedMime, imageFactory.MimeType);
Assert.IsNotNull(imageFactory.Image);
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Assert.AreEqual(file.FullName, imageFactory.ImagePath);
Assert.IsNotNull(imageFactory.Image);
}
}
}
/// <summary>
/// Tests the loading of image from a memory stream
/// </summary>
[Test]
public void TestLoadImageFromMemory()
{
foreach (FileInfo file in this.ListInputFiles())
{
byte[] photoBytes = File.ReadAllBytes(file.FullName);
using (MemoryStream inStream = new MemoryStream(photoBytes))
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(inStream);
Assert.AreEqual(null, imageFactory.ImagePath);
Assert.IsNotNull(imageFactory.Image);
}
}
}
}
/// <summary>
/// Tests that the save method actually saves a file
/// </summary>
[Test]
public void TestSaveToDisk()
{
foreach (FileInfo file in this.ListInputFiles())
{
string outputFileName = string.Format("./output/{0}", file.Name);
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
imageFactory.Save(outputFileName);
Assert.AreEqual(true, File.Exists(outputFileName));
}
}
}
/// <summary>
/// Tests that the save method actually writes to memory
/// </summary>
[Test]
public void TestSaveToMemory()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
using (MemoryStream s = new MemoryStream())
{
imageFactory.Save(s);
s.Seek(0, SeekOrigin.Begin);
Assert.AreEqual(true, s.Capacity > 0);
}
}
}
}
/// <summary>
/// Tests that a filter is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectAlpha()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Alpha(50);
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that brightness changes is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectBrightness()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Brightness(50);
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that background color changes are really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectBackgroundColor()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.BackgroundColor(Color.Yellow);
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a contrast change is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectContrast()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Contrast(50);
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a saturation change is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectSaturation()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Saturation(50);
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a tint change is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectTint()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Tint(Color.FromKnownColor(KnownColor.AliceBlue));
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a vignette change is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectVignette()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Vignette(Color.FromKnownColor(KnownColor.AliceBlue));
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a filter is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectWatermark()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Watermark(new Imaging.TextLayer
{
FontFamily = new FontFamily("Arial"),
FontSize = 10,
Position = new Point(10, 10),
Text = "Lorem ipsum dolor"
});
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a filter is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectBlur()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.GaussianBlur(5);
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a filter is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectBlurWithLayer()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.GaussianBlur(new Imaging.GaussianLayer { Sigma = 10, Size = 5, Threshold = 2 });
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a filter is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectSharpen()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.GaussianSharpen(5);
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that a filter is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestApplyEffectSharpenWithLayer()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.GaussianSharpen(new Imaging.GaussianLayer { Sigma = 10, Size = 5, Threshold = 2 });
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that all filters can be applied
/// </summary>
[Test]
public void TestApplyEffectFilter()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Filter(Imaging.Filters.MatrixFilters.BlackWhite);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.Comic);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.Gotham);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.GreyScale);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.HiSatch);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.Invert);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.Lomograph);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.LoSatch);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.Polaroid);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
imageFactory.Filter(Imaging.Filters.MatrixFilters.Sepia);
Assert.AreNotEqual(original, imageFactory.Image);
imageFactory.Reset();
}
}
}
/// <summary>
/// Tests that a filter is really applied by checking that the image is modified
/// </summary>
[Test]
public void TestRoundedCorners()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.RoundedCorners(new Imaging.RoundedCornerLayer(5));
Assert.AreNotEqual(original, imageFactory.Image);
}
}
}
/// <summary>
/// Tests that the image is well resized using constraints
/// </summary>
[Test]
public void TestResizeConstraints()
{
const int MaxSize = 200;
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
imageFactory.Constrain(new Size(MaxSize, MaxSize));
Assert.LessOrEqual(imageFactory.Image.Width, MaxSize);
Assert.LessOrEqual(imageFactory.Image.Height, MaxSize);
}
}
}
/// <summary>
/// Tests that the image is well cropped
/// </summary>
[Test]
public void TestCrop()
{
const int MaxSize = 20;
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Crop(new Rectangle(0, 0, MaxSize, MaxSize));
Assert.AreNotEqual(original, imageFactory.Image);
Assert.AreEqual(MaxSize, imageFactory.Image.Width);
Assert.LessOrEqual(MaxSize, imageFactory.Image.Height);
}
}
}
/// <summary>
/// Tests that the image is well cropped
/// </summary>
[Test]
public void TestCropWithCropLayer()
{
const int MaxSize = 20;
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Crop(new Imaging.CropLayer(0, 0, MaxSize, MaxSize, Imaging.CropMode.Pixels));
Assert.AreNotEqual(original, imageFactory.Image);
Assert.AreEqual(MaxSize, imageFactory.Image.Width);
Assert.LessOrEqual(MaxSize, imageFactory.Image.Height);
}
}
}
/// <summary>
/// Tests that the image is flipped
/// </summary>
[Test]
public void TestFlip()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Flip(true);
Assert.AreNotEqual(original, imageFactory.Image);
Assert.AreEqual(original.Width, imageFactory.Image.Width);
Assert.AreEqual(original.Height, imageFactory.Image.Height);
imageFactory.Reset();
imageFactory.Flip(false);
Assert.AreNotEqual(original, imageFactory.Image);
Assert.AreEqual(original.Width, imageFactory.Image.Width);
Assert.AreEqual(original.Height, imageFactory.Image.Height);
}
}
}
/// <summary>
/// Tests that the image is resized
/// </summary>
[Test]
public void TestResize()
{
const int NewSize = 150;
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
imageFactory.Resize(new Size(NewSize, NewSize));
Assert.AreEqual(NewSize, imageFactory.Image.Width);
Assert.AreEqual(NewSize, imageFactory.Image.Height);
}
}
}
/// <summary>
/// Tests that the image is resized
/// </summary>
[Test]
public void TestResizeWithLayer()
{
const int NewSize = 150;
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
imageFactory.Resize(new Imaging.ResizeLayer(new Size(NewSize, NewSize), Imaging.ResizeMode.Stretch, Imaging.AnchorPosition.Left));
Assert.AreEqual(NewSize, imageFactory.Image.Width);
Assert.AreEqual(NewSize, imageFactory.Image.Height);
}
}
}
/// <summary>
/// Tests that the image is resized
/// </summary>
[Test]
public void TestRotate()
{
foreach (FileInfo file in this.ListInputFiles())
{
using (ImageFactory imageFactory = new ImageFactory())
{
imageFactory.Load(file.FullName);
Image original = (Image)imageFactory.Image.Clone();
imageFactory.Rotate(90);
Assert.AreEqual(original.Height, imageFactory.Image.Width);
Assert.AreEqual(original.Width, imageFactory.Image.Height);
}
}
}
/// <summary>
/// Gets the files matching the given extensions.
/// </summary>
/// <param name="dir">The <see cref="System.IO.DirectoryInfo"/>.</param>
/// <param name="extensions">The extensions.</param>
/// <returns>A collection of <see cref="System.IO.FileInfo"/></returns>
/// <exception cref="System.ArgumentNullException">The extensions variable is null.</exception>
private static IEnumerable<FileInfo> GetFilesByExtensions(DirectoryInfo dir, params string[] extensions)
{
if (extensions == null)
{
throw new ArgumentNullException("extensions");
}
IEnumerable<FileInfo> files = dir.EnumerateFiles();
return files.Where(f => extensions.Contains(f.Extension, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// Lists the input files in the Images folder
/// </summary>
/// <returns>The list of files.</returns>
private IEnumerable<FileInfo> ListInputFiles()
{
if (this.images != null)
{
return this.images;
}
DirectoryInfo directoryInfo = new DirectoryInfo("./Images");
this.images = GetFilesByExtensions(directoryInfo, new[] { ".jpg", ".jpeg", ".png", ".gif", ".tiff", ".bmp", ".webp" });
return this.images;
}
}
}

88
src/ImageProcessor.UnitTests/ImageProcessor.UnitTests.csproj

@ -3,13 +3,18 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{03CA9055-F997-428C-BF28-F50F991777C6}</ProjectGuid>
<ProjectGuid>{633B1C4C-4823-47BE-9A01-A665F3118C8C}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>ImageProcessor.UnitTests</RootNamespace>
<AssemblyName>ImageProcessor.UnitTests</AssemblyName>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -20,6 +25,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
@ -28,6 +34,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -39,8 +46,11 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="RegularExpressionUnitTests.cs" />
<Compile Include="ImageFactoryUnitTests.cs" />
<Compile Include="ImageFactoryUnitTests.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
<Compile Include="Extensions\DoubleExtensionsUnitTests.cs" />
<Compile Include="Extensions\IntegerExtensionsUnitTests.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@ -51,92 +61,65 @@
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="..\Images\Chrysanthemum.jpg">
<Link>Images\Chrysanthemum.jpg</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Desert.jpg">
<Link>Images\Desert.jpg</Link>
<None Include="Images\autorotate.jpg" />
<None Include="Images\cmyk-profile-euroscale.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Hydrangeas.jpg">
<Link>Images\Hydrangeas.jpg</Link>
<None Include="Images\cmyk.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Jellyfish.jpg">
<Link>Images\Jellyfish.jpg</Link>
<None Include="Images\color-vision-test.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Koala.jpg">
<Link>Images\Koala.jpg</Link>
<None Include="Images\exif-Tulips.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Lighthouse.jpg">
<Link>Images\Lighthouse.jpg</Link>
<None Include="Images\exif-rocks.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Penguins-200.jpg">
<Link>Images\Penguins-200.jpg</Link>
<None Include="Images\format-Penguins-8bit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Penguins-8.png">
<Link>Images\Penguins-8.png</Link>
<None Include="Images\format-Penguins.bmp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Penguins.bmp">
<Link>Images\Penguins.bmp</Link>
<None Include="Images\format-Penguins.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Penguins.gif">
<Link>Images\Penguins.gif</Link>
<None Include="Images\format-Penguins.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Penguins.jpg">
<Link>Images\Penguins.jpg</Link>
<None Include="Images\format-Penguins.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Penguins.png">
<Link>Images\Penguins.png</Link>
<None Include="Images\format-Penguins.tif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Penguins.tif">
<Link>Images\Penguins.tif</Link>
<None Include="Images\format-animated.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\Tulips.jpg">
<Link>Images\Tulips.jpg</Link>
<None Include="Images\hi-color.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\bus.jpg">
<Link>Images\bus.jpg</Link>
<None Include="Images\hi-contrast.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\cmyk.jpg">
<Link>Images\cmyk.jpg</Link>
<None Include="Images\hi-saturation.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\cmyk.png">
<Link>Images\cmyk.png</Link>
<None Include="Images\profile-adobe-rgb.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\jrt.jpg">
<Link>Images\jrt.jpg</Link>
<None Include="Images\profile-srgb.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\meter.gif">
<Link>Images\meter.gif</Link>
<None Include="Images\size-Penguins-200.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\rocks.jpg">
<Link>Images\rocks.jpg</Link>
<None Include="Images\text-over-transparent.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\rotate.jpg">
<Link>Images\rotate.jpg</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\sample1.jpg">
<Link>Images\sample1.jpg</Link>
<None Include="Images\udendørs.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\Images\srgb.jpg">
@ -172,4 +155,7 @@
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<ItemGroup />
<ItemGroup />
<ItemGroup />
</Project>

1
src/ImageProcessor.UnitTests/Images/autorotate.jpg.REMOVED.git-id

@ -0,0 +1 @@
75b37593bb2e505bf4fbe874eaf30debd6161c2e

1
src/ImageProcessor.UnitTests/Images/cmyk-profile-euroscale.jpg.REMOVED.git-id

@ -0,0 +1 @@
d0a1a39a6729e826098ae5e987c22c34c989b7e3

1
src/ImageProcessor.UnitTests/Images/cmyk.jpg.REMOVED.git-id

@ -0,0 +1 @@
9160894da31fedebb1fcd64eb57ca173187c63a6

1
src/ImageProcessor.UnitTests/Images/color-vision-test.gif.REMOVED.git-id

@ -0,0 +1 @@
b169fac4f1591e81e91c0bb6fed6dcf62a34c80e

1
src/ImageProcessor.UnitTests/Images/exif-Tulips.jpg.REMOVED.git-id

@ -0,0 +1 @@
9d7e7964a2285363171929315b15ec69f14831ef

1
src/ImageProcessor.UnitTests/Images/exif-rocks.jpg.REMOVED.git-id

@ -0,0 +1 @@
be31c9c0dea90586e2965208611fad024f6a5b08

1
src/ImageProcessor.UnitTests/Images/format-Penguins-8bit.png.REMOVED.git-id

@ -0,0 +1 @@
51ccec74a0351599de104f166b32d2860acaf089

1
src/ImageProcessor.UnitTests/Images/format-Penguins.bmp.REMOVED.git-id

@ -0,0 +1 @@
d7adbea2db4e3388541e31a229e5741677aaa7fd

1
src/ImageProcessor.UnitTests/Images/format-Penguins.gif.REMOVED.git-id

@ -0,0 +1 @@
b301e58d431a78d3f17be53be1cdc94c86286389

1
src/ImageProcessor.UnitTests/Images/format-Penguins.jpg.REMOVED.git-id

@ -0,0 +1 @@
ee5a15e7f8fc2655d5c1fc736a05857ab3d885bd

1
src/ImageProcessor.UnitTests/Images/format-Penguins.png.REMOVED.git-id

@ -0,0 +1 @@
b6434b5a35e989d4fa71ede1a8316fedeee7ae5f

1
src/ImageProcessor.UnitTests/Images/format-Penguins.tif.REMOVED.git-id

@ -0,0 +1 @@
c789aaec248568c24394b05c02db4233e0c5a4eb

1
src/ImageProcessor.UnitTests/Images/format-animated.gif.REMOVED.git-id

@ -0,0 +1 @@
a41fb1117e1d730a4a488dcb67e0b867aa3c614e

BIN
src/ImageProcessor.UnitTests/Images/hi-color.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
src/ImageProcessor.UnitTests/Images/hi-contrast.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
src/ImageProcessor.UnitTests/Images/hi-saturation.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

1
src/ImageProcessor.UnitTests/Images/profile-adobe-rgb.jpg.REMOVED.git-id

@ -0,0 +1 @@
189f79f9b9604c5413aba928662d84edd426142d

1
src/ImageProcessor.UnitTests/Images/profile-srgb.jpg.REMOVED.git-id

@ -0,0 +1 @@
f731bdf700d2718f528317263264e5466374c5e5

BIN
src/ImageProcessor.UnitTests/Images/size-Penguins-200.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
src/ImageProcessor.UnitTests/Images/text-over-transparent.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
src/ImageProcessor.UnitTests/Images/udendørs.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

9
src/TestWebsites/NET45/Test_Website_Webforms_NET45/Properties/AssemblyInfo.cs → src/ImageProcessor.UnitTests/Properties/AssemblyInfo.cs

@ -5,11 +5,11 @@ 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("Test_Website_Webforms_NET45")]
[assembly: AssemblyTitle("ImageProcessor.UnitTests2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Test_Website_Webforms_NET45")]
[assembly: AssemblyProduct("ImageProcessor.UnitTests2")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0f9a8d30-dcae-41a0-9543-ef2ec5e2ca6d")]
[assembly: Guid("3003f5a6-eb11-4eee-bfaa-448deb3a31d9")]
// Version information for an assembly consists of the following four values:
//
@ -29,7 +29,8 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// 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")]

217
src/ImageProcessor.UnitTests/RegularExpressionUnitTests.cs

@ -1,217 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="RegularExpressionUnitTests.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Unit tests for the ImageProcessor regular expressions
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.UnitTests
{
using System;
using System.Drawing;
using ImageProcessor.Imaging;
using ImageProcessor.Processors;
using NUnit.Framework;
/// <summary>
/// Test harness for the regular expressions
/// </summary>
[TestFixture]
public class RegularExpressionUnitTests
{
/// <summary>
/// The alpha regex unit test.
/// </summary>
[Test]
public void TestAlphaRegex()
{
const string Querystring = "alpha=56";
const int Expected = 56;
Alpha alpha = new Alpha();
alpha.MatchRegexIndex(Querystring);
int actual = alpha.DynamicParameter;
Assert.AreEqual(Expected, actual);
}
/// <summary>
/// The brightness regex unit test.
/// </summary>
[Test]
public void TestBrightnessRegex()
{
const string Querystring = "brightness=56";
const int Expected = 56;
Brightness brightness = new Brightness();
brightness.MatchRegexIndex(Querystring);
int actual = brightness.DynamicParameter;
Assert.AreEqual(Expected, actual);
}
/// <summary>
/// The contrast regex unit test.
/// </summary>
[Test]
public void TestContrastRegex()
{
const string Querystring = "contrast=56";
const int Expected = 56;
Contrast contrast = new Contrast();
contrast.MatchRegexIndex(Querystring);
int actual = contrast.DynamicParameter;
Assert.AreEqual(Expected, actual);
}
/// <summary>
/// The rotate regex unit test.
/// </summary>
[Test]
public void TestCropRegex()
{
const string Querystring = "crop=0,0,150,300";
CropLayer expected = new CropLayer(0, 0, 150, 300, CropMode.Pixels);
Crop crop = new Crop();
crop.MatchRegexIndex(Querystring);
CropLayer actual = crop.DynamicParameter;
Assert.AreEqual(expected, actual);
}
/// <summary>
/// The filter regex unit test.
/// </summary>
[Test]
public void TestFilterRegex()
{
// Should really write more for the other filters.
const string Querystring = "filter=lomograph";
const string Expected = "lomograph";
Filter filter = new Filter();
filter.MatchRegexIndex(Querystring);
string actual = filter.DynamicParameter;
Assert.AreEqual(Expected, actual);
}
/// <summary>
/// The format regex unit test.
/// </summary>
[Test]
public void TestFormatRegex()
{
const string Querystring = "format=gif";
const string Expected = "gif";
Format format = new Format();
format.MatchRegexIndex(Querystring);
string actual = format.DynamicParameter;
Assert.AreEqual(Expected, actual);
}
/// <summary>
/// The quality regex unit test.
/// </summary>
[Test]
public void TestQualityRegex()
{
const string Querystring = "quality=56";
const int Expected = 56;
Quality quality = new Quality();
quality.MatchRegexIndex(Querystring);
int actual = quality.DynamicParameter;
Assert.AreEqual(Expected, actual);
}
/// <summary>
/// The resize regex unit test.
/// </summary>
[Test]
public void TestResizeRegex()
{
const string Querystring = "width=300";
ResizeLayer expected = new ResizeLayer(new Size(300, 0));
Resize resize = new Resize();
resize.MatchRegexIndex(Querystring);
ResizeLayer actual = resize.DynamicParameter;
Assert.AreEqual(expected, actual);
}
/// <summary>
/// The rotate regex unit test.
/// </summary>
[Test]
public void TestRotateRegex()
{
const string Querystring = "rotate=270";
RotateLayer expected = new RotateLayer(270, Color.Transparent);
Rotate rotate = new Rotate();
rotate.MatchRegexIndex(Querystring);
RotateLayer actual = rotate.DynamicParameter;
Assert.AreEqual(expected, actual);
}
/// <summary>
/// The rounded corners regex unit test.
/// </summary>
[Test]
public void TestRoundedCornersRegex()
{
const string Querystring = "roundedcorners=30";
RoundedCornerLayer expected = new RoundedCornerLayer(30, true, true, true, true);
RoundedCorners roundedCorners = new RoundedCorners();
roundedCorners.MatchRegexIndex(Querystring);
RoundedCornerLayer actual = roundedCorners.DynamicParameter;
Assert.AreEqual(expected, actual);
}
/// <summary>
/// The rounded corners regex unit test.
/// </summary>
[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);
Tint tint = new Tint();
tint.MatchRegexIndex(HexQuerystring);
Color actualHex = tint.DynamicParameter;
Assert.AreEqual(expectedHex, actualHex);
tint = new Tint();
tint.MatchRegexIndex(RgbaQuerystring);
Color actualRgba = tint.DynamicParameter;
Assert.AreEqual(expectedRgba, actualRgba);
}
}
}

2
src/ImageProcessor.UnitTests/packages.config

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="2.6.3" targetFramework="net40" />
<package id="NUnit" version="2.6.3" targetFramework="net451" />
</packages>

160
src/ImageProcessor.Web.UnitTests/Extensions/StringExtensionsUnitTests.cs

@ -0,0 +1,160 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StringExtensionsUnitTests.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Test harness for the string extensions
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.UnitTests.Extensions
{
using System;
using System.Collections.Generic;
using ImageProcessor.Web.Extensions;
using NUnit.Framework;
/// <summary>
/// Test harness for the string extensions
/// </summary>
[TestFixture]
public class StringExtensionsUnitTests
{
/// <summary>
/// Tests the passing to an integer array
/// </summary>
[Test]
public void TestToIntegerArray()
{
Dictionary<string, int[]> data = new Dictionary<string, int[]>
{
{
"123-456,78-90",
new[] { 123, 456, 78, 90 }
},
{
"87390174,741897498,74816,748297,57355",
new[]
{
87390174, 741897498, 74816,
748297, 57355
}
},
{ "1-2-3", new[] { 1, 2, 3 } }
};
foreach (KeyValuePair<string, int[]> item in data)
{
int[] result = item.Key.ToPositiveIntegerArray();
Assert.AreEqual(item.Value, result);
}
}
/// <summary>
/// Tests the passing to an float array
/// </summary>
[Test]
public void TestToFloatArray()
{
Dictionary<string, float[]> data = new Dictionary<string, float[]>
{
{
"12.3-4.56,78-9.0",
new[] { 12.3F, 4.56F, 78, 9 }
},
{
"87390.174,7.41897498,748.16,748297,5.7355",
new[]
{
87390.174F, 7.41897498F,
748.16F, 748297, 5.7355F
}
},
{ "1-2-3", new float[] { 1, 2, 3 } }
};
foreach (KeyValuePair<string, float[]> item in data)
{
float[] result = item.Key.ToPositiveFloatArray();
Assert.AreEqual(item.Value, result);
}
}
/// <summary>
/// Tests the MD5 fingerprint
/// </summary>
/// <param name="input">The input value</param>
/// <param name="expected">The expected output of the hash</param>
[Test]
[TestCase("test input", "2e7f7a62eabf0993239ca17c78c464d9")]
[TestCase("lorem ipsum dolor", "96ee002fee25e8b675a477c9750fa360")]
[TestCase("LoReM IpSuM DoLoR", "41e201da794c7fbdb8ce5526a71c8c83")]
[TestCase("1234567890", "e15e31c3d8898c92ab172a4311be9e84")]
public void TestToMd5Fingerprint(string input, string expected)
{
string result = input.ToMD5Fingerprint();
bool comparison = result.Equals(expected, StringComparison.InvariantCultureIgnoreCase);
Assert.True(comparison);
}
/// <summary>
/// Tests the SHA-1 fingerprint
/// </summary>
/// <param name="input">The input value</param>
/// <param name="expected">The expected output of the hash</param>
[Test]
[TestCase("test input", "49883b34e5a0f48224dd6230f471e9dc1bdbeaf5")]
[TestCase("lorem ipsum dolor", "75899ad8827a32493928903aecd6e931bf36f967")]
[TestCase("LoReM IpSuM DoLoR", "2f44519afae72fc0837b72c6b53cb11338a1f916")]
[TestCase("1234567890", "01b307acba4f54f55aafc33bb06bbbf6ca803e9a")]
public void TestToSHA1Fingerprint(string input, string expected)
{
string result = input.ToSHA1Fingerprint();
bool comparison = result.Equals(expected, StringComparison.InvariantCultureIgnoreCase);
Assert.True(comparison);
}
/// <summary>
/// Tests if the value is a valid URI path name. I.E the path part of a uri.
/// </summary>
/// <param name="input">The value to test</param>
/// <param name="expected">Whether the value is correct</param>
/// <remarks>
/// The full RFC3986 does not seem to pass the test with the square brackets
/// ':' is failing for some reason in VS but not elsewhere. Could be a build issue.
/// </remarks>
[Test]
[TestCase("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", true)]
[TestCase("-", true)]
[TestCase(".", true)]
[TestCase("_", true)]
[TestCase("~", true)]
[TestCase(":", true)]
[TestCase("/", true)]
[TestCase("?", true)]
[TestCase("#", false)]
[TestCase("[", false)]
[TestCase("]", false)]
[TestCase("@", true)]
[TestCase("!", true)]
[TestCase("$", true)]
[TestCase("&", true)]
[TestCase("'", true)]
[TestCase("(", true)]
[TestCase(")", true)]
[TestCase("*", true)]
[TestCase("+", true)]
[TestCase(",", true)]
[TestCase(";", true)]
[TestCase("=", true)]
[TestCase("lorem ipsum", false)]
[TestCase("é", false)]
public void TestIsValidUriPathName(string input, bool expected)
{
bool result = input.IsValidVirtualPathName();
Assert.AreEqual(expected, result);
}
}
}

112
src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{961340C8-8C93-401D-A0A2-FF9EC61E5260}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ImageProcessor.Web.UnitTests</RootNamespace>
<AssemblyName>ImageProcessor.Web.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="Extensions\StringExtensionsUnitTests.cs" />
<Compile Include="RegularExpressionUnitTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImageProcessor.Web\ImageProcessor.Web.csproj">
<Project>{d011a778-59c8-4bfa-a770-c350216bf161}</Project>
<Name>ImageProcessor.Web</Name>
</ProjectReference>
<ProjectReference Include="..\ImageProcessor\ImageProcessor.csproj">
<Project>{3b5dd734-fb7a-487d-8ce6-55e7af9aea7e}</Project>
<Name>ImageProcessor</Name>
</ProjectReference>
<ProjectReference Include="..\Plugins\ImageProcessor\ImageProcessor.Plugins.WebP\ImageProcessor.Plugins.WebP.csproj">
<Project>{2cf69699-959a-44dc-a281-4e2596c25043}</Project>
<Name>ImageProcessor.Plugins.WebP</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

11
src/TestWebsites/NET4/Properties/AssemblyInfo.cs → src/ImageProcessor.Web.UnitTests/Properties/AssemblyInfo.cs

@ -5,12 +5,12 @@ 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("Test")]
[assembly: AssemblyTitle("ImageProcessor.Web.UnitTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Test")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyProduct("ImageProcessor.Web.UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("34f4c859-67cc-40d2-97ae-27e8c7157052")]
[assembly: Guid("74919969-6a75-4d26-b1b8-0626aa10af6d")]
// Version information for an assembly consists of the following four values:
//
@ -29,7 +29,8 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// 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")]

474
src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs

@ -0,0 +1,474 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="RegularExpressionUnitTests.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Unit tests for the ImageProcessor regular expressions
// </summary>
// --------------------------------------------------------------------------------------------------------------------
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;
/// <summary>
/// Test harness for the regular expressions
/// </summary>
[TestFixture]
public class RegularExpressionUnitTests
{
/// <summary>
/// The alpha regex unit test.
/// </summary>
/// <param name="input">
/// The input string.
/// </param>
/// <param name="expected">
/// The expected result.
/// </param>
[Test]
[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)
{
Processors.Alpha alpha = new Processors.Alpha();
alpha.MatchRegexIndex(input);
int result = alpha.Processor.DynamicParameter;
Assert.AreEqual(expected, result);
}
/// <summary>
/// The contrast regex unit test.
/// </summary>
/// <param name="input">
/// The input string.
/// </param>
/// <param name="expected">
/// The expected result.
/// </param>
[Test]
[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)
{
Processors.Brightness brightness = new Processors.Brightness();
brightness.MatchRegexIndex(input);
int result = brightness.Processor.DynamicParameter;
Assert.AreEqual(expected, result);
}
/// <summary>
/// The contrast regex unit test.
/// </summary>
/// <param name="input">
/// The input string.
/// </param>
/// <param name="expected">
/// The expected result.
/// </param>
[Test]
[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)
{
Processors.Contrast contrast = new Processors.Contrast();
contrast.MatchRegexIndex(input);
int result = contrast.Processor.DynamicParameter;
Assert.AreEqual(expected, result);
}
/// <summary>
/// The rotate regex unit test.
/// </summary>
[Test]
public void TestCropRegex()
{
const string Querystring = "crop=0,0,150,300";
CropLayer expected = new CropLayer(0, 0, 150, 300, CropMode.Pixels);
Processors.Crop crop = new Processors.Crop();
crop.MatchRegexIndex(Querystring);
CropLayer actual = crop.Processor.DynamicParameter;
Assert.AreEqual(expected, actual);
}
/// <summary>
/// The filter regex unit test.
/// </summary>
[Test]
public void TestFilterRegex()
{
Dictionary<string, IMatrixFilter> data = new Dictionary<string, IMatrixFilter>
{
{
"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();
foreach (KeyValuePair<string, IMatrixFilter> item in data)
{
filter.MatchRegexIndex(item.Key);
IMatrixFilter result = filter.Processor.DynamicParameter;
Assert.AreEqual(item.Value, result);
}
}
/// <summary>
/// The format regex unit test.
/// </summary>
/// <param name="input">
/// The input querystring.
/// </param>
/// <param name="expected">
/// The expected type.
/// </param>
[Test]
[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)
{
Processors.Format format = new Processors.Format();
format.MatchRegexIndex(input);
Type result = format.Processor.DynamicParameter.GetType();
Assert.AreEqual(expected, result);
}
/// <summary>
/// The quality regex unit test.
/// </summary>
/// <param name="input">
/// The input.
/// </param>
/// <param name="expected">
/// The expected result.
/// </param>
[Test]
[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)
{
Processors.Quality quality = new Processors.Quality();
quality.MatchRegexIndex(input);
int result = quality.Processor.DynamicParameter;
Assert.AreEqual(expected, result);
}
/// <summary>
/// The meta regex unit test.
/// </summary>
/// <param name="input">
/// The input.
/// </param>
/// <param name="expected">
/// The expected result.
/// </param>
[Test]
[TestCase("meta=true", true)]
[TestCase("meta=false", false)]
public void TestMetaRegex(string input, bool expected)
{
Processors.Meta meta = new Processors.Meta();
meta.MatchRegexIndex(input);
bool result = meta.Processor.DynamicParameter;
Assert.AreEqual(expected, result);
}
/// <summary>
/// The resize regex unit test.
/// </summary>
[Test]
public void TestResizeRegex()
{
Dictionary<string, ResizeLayer> data = new Dictionary<string, ResizeLayer>
{
{
"width=300", new ResizeLayer(new Size(300, 0))
},
{
"height=300", new ResizeLayer(new Size(0, 300))
},
{
"height=300&mode=crop", new ResizeLayer(new Size(0, 300), ResizeMode.Crop)
},
{
"width=300&mode=crop", new ResizeLayer(new Size(300, 0), ResizeMode.Crop)
},
{
"width=600&heightratio=0.416", new ResizeLayer(new Size(600, 250))
},
{
"width=600&height=250&mode=max", new ResizeLayer(new Size(600, 250), ResizeMode.Max)
}
};
Processors.Resize resize = new Processors.Resize();
foreach (KeyValuePair<string, ResizeLayer> item in data)
{
resize.MatchRegexIndex(item.Key);
ResizeLayer result = resize.Processor.DynamicParameter;
Assert.AreEqual(item.Value, result);
}
}
/// <summary>
/// The rotate regex unit test.
/// </summary>
/// <param name="input">
/// The input string.
/// </param>
/// <param name="expected">
/// The expected result.
/// </param>
[Test]
[TestCase("rotate=0", 0)]
[TestCase("rotate=270", 270)]
[TestCase("rotate=-270", 0)]
[TestCase("rotate=angle-28", 28)]
public void TestRotateRegex(string input, int expected)
{
Processors.Rotate rotate = new Processors.Rotate();
rotate.MatchRegexIndex(input);
int result = rotate.Processor.DynamicParameter;
Assert.AreEqual(expected, result);
}
/// <summary>
/// The rounded corners regex unit test.
/// </summary>
[Test]
public void TestRoundedCornersRegex()
{
Dictionary<string, RoundedCornerLayer> data = new Dictionary<string, RoundedCornerLayer>
{
{
"roundedcorners=30", new RoundedCornerLayer(30)
},
{
"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)
}
};
Processors.RoundedCorners roundedCorners = new Processors.RoundedCorners();
foreach (KeyValuePair<string, RoundedCornerLayer> item in data)
{
roundedCorners.MatchRegexIndex(item.Key);
RoundedCornerLayer result = roundedCorners.Processor.DynamicParameter;
Assert.AreEqual(item.Value, result);
}
}
/// <summary>
/// The saturation regex unit test.
/// </summary>
/// <param name="input">
/// The input string.
/// </param>
/// <param name="expected">
/// The expected result.
/// </param>
[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);
}
/// <summary>
/// The tint regex unit test.
/// </summary>
[Test]
public void TestTintRegex()
{
Dictionary<string, Color> data = new Dictionary<string, Color>
{
{
"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();
foreach (KeyValuePair<string, Color> item in data)
{
tint.MatchRegexIndex(item.Key);
Color result = tint.Processor.DynamicParameter;
Assert.AreEqual(item.Value, result);
}
}
/// <summary>
/// The vignette regex unit test.
/// </summary>
[Test]
public void TestVignetteRegex()
{
Dictionary<string, Color> data = new Dictionary<string, Color>
{
{
"vignette", Color.Black
},
{
"vignette=true", Color.Black
},
{
"vignette=6aa6cc", ColorTranslator.FromHtml("#" + "6aa6cc")
},
{
"vignette=106,166,204,255", Color.FromArgb(255, 106, 166, 204)
},
{
"vignette=fff", Color.FromArgb(255, 255, 255, 255)
},
{
"vignette=white", Color.White
}
};
Processors.Vignette vignette = new Processors.Vignette();
foreach (KeyValuePair<string, Color> item in data)
{
vignette.MatchRegexIndex(item.Key);
Color result = vignette.Processor.DynamicParameter;
Assert.AreEqual(item.Value, result);
}
}
/// <summary>
/// The watermark regex unit test.
/// </summary>
[Test]
public void TestWaterMarkRegex()
{
Dictionary<string, TextLayer> data = new Dictionary<string, TextLayer>
{
{
"watermark=text-watermark goodness,color-fff,size-36,style-italic,opacity-80,position-30,150,shadow-true,font-arial",
new TextLayer
{
Text = "watermark goodness",
FontColor = ColorTranslator.FromHtml("#" + "ffffff"),
FontSize = 36,
Style = FontStyle.Italic,
Opacity = 80,
Position = new Point(30, 150),
DropShadow = true,
FontFamily = new FontFamily("arial")
}
},
{
"watermark=watermark goodness&color=fff&fontsize=36&fontstyle=italic&fontopacity=80&textposition=30,150&textshadow=true&fontfamily=arial",
new TextLayer
{
Text = "watermark goodness",
FontColor = ColorTranslator.FromHtml("#" + "ffffff"),
FontSize = 36,
Style = FontStyle.Italic,
Opacity = 80,
Position = new Point(30, 150),
DropShadow = true,
FontFamily = new FontFamily("arial")
}
}
};
Processors.Watermark watermark = new Processors.Watermark();
foreach (KeyValuePair<string, TextLayer> item in data)
{
watermark.MatchRegexIndex(item.Key);
TextLayer result = watermark.Processor.DynamicParameter;
Assert.AreEqual(item.Value, result);
}
}
}
}

4
src/ImageProcessor.Web.UnitTests/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="2.6.3" targetFramework="net451" />
</packages>

55
src/ImageProcessor.Web/NET45/Caching/CacheIndexer.cs → src/ImageProcessor.Web/Caching/CacheIndexer.cs

@ -10,13 +10,9 @@
namespace ImageProcessor.Web.Caching
{
#region Using
using System.Collections.Generic;
using System.IO;
using System.Runtime.Caching;
using System.Threading.Tasks;
using ImageProcessor.Web.Helpers;
#endregion
/// <summary>
/// Represents an in memory collection of keys and values whose operations are concurrent.
@ -34,19 +30,32 @@ namespace ImageProcessor.Web.Caching
/// The <see cref="CachedImage"/> matching the given key if the <see cref="CacheIndexer"/> contains an element with
/// the specified key; otherwise, null.
/// </returns>
public static async Task<CachedImage> GetValueAsync(string cachedPath)
public static CachedImage GetValue(string cachedPath)
{
string key = Path.GetFileNameWithoutExtension(cachedPath);
CachedImage cachedImage = (CachedImage)MemCache.GetItem(key);
if (cachedImage == null)
{
cachedImage = await TaskHelpers.Run(() => GetCachedImage(cachedPath));
// FileInfo is thread safe.
FileInfo fileInfo = new FileInfo(cachedPath);
if (cachedImage != null)
if (!fileInfo.Exists)
{
Add(cachedImage);
return null;
}
// Pull the latest info.
fileInfo.Refresh();
cachedImage = new CachedImage
{
Key = Path.GetFileNameWithoutExtension(cachedPath),
Path = cachedPath,
CreationTimeUtc = fileInfo.CreationTimeUtc
};
Add(cachedImage);
}
return cachedImage;
@ -87,35 +96,5 @@ namespace ImageProcessor.Web.Caching
return cachedImage;
}
#endregion
/// <summary>
/// Creates a new cached image from the cache instance on disk.
/// </summary>
/// <param name="cachePath">
/// The cache path.
/// </param>
/// <returns>
/// The <see cref="CachedImage"/> from the cache instance on disk.
/// </returns>
private static CachedImage GetCachedImage(string cachePath)
{
// FileInfo is thread safe.
FileInfo fileInfo = new FileInfo(cachePath);
if (!fileInfo.Exists)
{
return null;
}
// Pull the latest info.
fileInfo.Refresh();
return new CachedImage
{
Key = Path.GetFileNameWithoutExtension(cachePath),
Path = cachePath,
CreationTimeUtc = fileInfo.CreationTimeUtc
};
}
}
}

2
src/ImageProcessor.Web/NET45/Caching/CachedImage.cs → src/ImageProcessor.Web/Caching/CachedImage.cs

@ -10,9 +10,7 @@
namespace ImageProcessor.Web.Caching
{
#region Using
using System;
#endregion
/// <summary>
/// Describes a cached image

226
src/ImageProcessor.Web/NET45/Caching/DiskCache.cs → src/ImageProcessor.Web/Caching/DiskCache.cs

@ -1,4 +1,4 @@
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DiskCache.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
@ -13,15 +13,12 @@ namespace ImageProcessor.Web.Caching
#region Using
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using ImageProcessor.Extensions;
using ImageProcessor.Web.Config;
using ImageProcessor.Web.Configuration;
using ImageProcessor.Web.Extensions;
using ImageProcessor.Web.Helpers;
#endregion
@ -35,29 +32,29 @@ namespace ImageProcessor.Web.Caching
/// <summary>
/// The maximum number of days to cache files on the system for.
/// </summary>
internal static readonly int MaxFileCachedDuration = ImageProcessorConfig.Instance.MaxCacheDays;
internal static readonly int MaxFileCachedDuration = ImageProcessorConfiguration.Instance.MaxCacheDays;
/// <summary>
/// The maximum number of files allowed in the directory.
/// </summary>
/// <remarks>
/// NTFS directories can handle up to 10,000 files in the directory before slowing down.
/// NTFS directories can handle up to 10,000 files in the directory before slowing down.
/// This will help us to ensure that don't go over that limit.
/// <see cref="http://stackoverflow.com/questions/197162/ntfs-performance-and-large-volumes-of-files-and-directories"/>
/// <see cref="http://stackoverflow.com/questions/115882/how-do-you-deal-with-lots-of-small-files"/>
/// <see cref="http://stackoverflow.com/questions/1638219/millions-of-small-graphics-files-and-how-to-overcome-slow-file-system-access-on"/>
/// <see href="http://stackoverflow.com/questions/197162/ntfs-performance-and-large-volumes-of-files-and-directories"/>
/// <see href="http://stackoverflow.com/questions/115882/how-do-you-deal-with-lots-of-small-files"/>
/// <see href="http://stackoverflow.com/questions/1638219/millions-of-small-graphics-files-and-how-to-overcome-slow-file-system-access-on"/>
/// </remarks>
private const int MaxFilesCount = 100;
/// <summary>
/// The virtual cache path.
/// </summary>
private static readonly string VirtualCachePath = ImageProcessorConfig.Instance.VirtualCachePath;
private static readonly string VirtualCachePath = ImageProcessorConfiguration.Instance.VirtualCachePath;
/// <summary>
/// The absolute path to virtual cache path on the server.
/// </summary>
private static readonly string AbsoluteCachePath = HostingEnvironment.MapPath(ImageProcessorConfig.Instance.VirtualCachePath);
private static readonly string AbsoluteCachePath = HostingEnvironment.MapPath(ImageProcessorConfiguration.Instance.VirtualCachePath);
/// <summary>
/// The request path for the image.
@ -74,11 +71,6 @@ namespace ImageProcessor.Web.Caching
/// </summary>
private readonly string imageName;
/// <summary>
/// Whether the request is for a remote image.
/// </summary>
private readonly bool isRemote;
/// <summary>
/// The physical cached path.
/// </summary>
@ -103,22 +95,17 @@ namespace ImageProcessor.Web.Caching
/// <param name="imageName">
/// The image name.
/// </param>
/// <param name="isRemote">
/// Whether the request is for a remote image.
/// </param>
public DiskCache(string requestPath, string fullPath, string imageName, bool isRemote)
public DiskCache(string requestPath, string fullPath, string imageName)
{
this.requestPath = requestPath;
this.fullPath = fullPath;
this.imageName = imageName;
this.isRemote = isRemote;
// Get the physical and virtual paths.
this.GetCachePaths();
}
#endregion
#region Properties
/// <summary>
/// Gets the cached path.
/// </summary>
@ -140,20 +127,73 @@ namespace ImageProcessor.Web.Caching
return this.virtualCachedPath;
}
}
#endregion
#region Methods
#region Internal
#region Public
/// <summary>
/// Trims a cached folder ensuring that it does not exceed the maximum file count.
/// </summary>
/// <param name="path">
/// The path to the folder.
/// </param>
public static void TrimCachedFolders(string path)
{
string directory = Path.GetDirectoryName(path);
if (directory != null)
{
DirectoryInfo directoryInfo = new DirectoryInfo(directory);
DirectoryInfo parentDirectoryInfo = directoryInfo.Parent;
if (parentDirectoryInfo != null)
{
// UNC folders can throw exceptions if the file doesn't exist.
foreach (DirectoryInfo enumerateDirectory in parentDirectoryInfo.SafeEnumerateDirectories())
{
IEnumerable<FileInfo> files = enumerateDirectory.EnumerateFiles().OrderBy(f => f.CreationTimeUtc);
int count = files.Count();
foreach (FileInfo fileInfo in files)
{
try
{
// If the group count is equal to the max count minus 1 then we know we
// have reduced the number of items below the maximum allowed.
// We'll cleanup any orphaned expired files though.
if (!IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1)
{
break;
}
// Remove from the cache and delete each CachedImage.
CacheIndexer.Remove(fileInfo.Name);
fileInfo.Delete();
count -= 1;
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
// Do nothing; skip to the next file.
}
}
}
}
}
}
/// <summary>
/// Adds an image to the cache.
/// </summary>
internal void AddImageToCache()
/// <param name="cachedPath">
/// The path to the cached image.
/// </param>
public void AddImageToCache(string cachedPath)
{
string key = Path.GetFileNameWithoutExtension(this.CachedPath);
string key = Path.GetFileNameWithoutExtension(cachedPath);
CachedImage cachedImage = new CachedImage
{
Key = key,
Path = this.CachedPath,
Path = cachedPath,
CreationTimeUtc = DateTime.UtcNow
};
@ -163,119 +203,58 @@ namespace ImageProcessor.Web.Caching
/// <summary>
/// Returns a value indicating whether the original file is new or has been updated.
/// </summary>
/// <param name="cachedPath">
/// The path to the cached image.
/// </param>
/// <returns>
/// True if the the original file is new or has been updated; otherwise, false.
/// </returns>
internal async Task<bool> IsNewOrUpdatedFileAsync()
public bool IsNewOrUpdatedFile(string cachedPath)
{
string path = this.CachedPath;
bool isUpdated = false;
CachedImage cachedImage;
CachedImage cachedImage = CacheIndexer.GetValue(cachedPath);
if (this.isRemote)
if (cachedImage == null)
{
cachedImage = await CacheIndexer.GetValueAsync(path);
if (cachedImage != null)
{
// Can't check the last write time so check to see if the cached image is set to expire
// or if the max age is different.
if (this.IsExpired(cachedImage.CreationTimeUtc))
{
CacheIndexer.Remove(path);
isUpdated = true;
}
}
else
{
// Nothing in the cache so we should return true.
isUpdated = true;
}
// Nothing in the cache so we should return true.
isUpdated = true;
}
else
{
// Test now for locally requested files.
cachedImage = await CacheIndexer.GetValueAsync(path);
if (cachedImage == null)
// Check to see if the cached image is set to expire.
if (IsExpired(cachedImage.CreationTimeUtc))
{
// Nothing in the cache so we should return true.
CacheIndexer.Remove(cachedPath);
isUpdated = true;
}
}
return isUpdated;
}
/// <summary>
/// Trims a cached folder ensuring that it does not exceed the maximum file count.
/// </summary>
/// <param name="path">
/// The path to the folder.
/// </param>
/// <returns>
/// The <see cref="T:System.Threading.Tasks.Task"/>.
/// </returns>
internal async Task TrimCachedFolderAsync(string path)
{
await TaskHelpers.Run(() => this.TrimCachedFolders(path));
}
#endregion
#region Private
/// <summary>
/// Trims a cached folder ensuring that it does not exceed the maximum file count.
/// Gets a value indicating whether the given images creation date is out with
/// the prescribed limit.
/// </summary>
/// <param name="path">
/// The path to the folder.
/// <param name="creationDate">
/// The creation date.
/// </param>
private void TrimCachedFolders(string path)
/// <returns>
/// The true if the date is out with the limit, otherwise; false.
/// </returns>
private static bool IsExpired(DateTime creationDate)
{
string directory = Path.GetDirectoryName(path);
if (directory != null)
{
DirectoryInfo directoryInfo = new DirectoryInfo(directory);
DirectoryInfo parentDirectoryInfo = directoryInfo.Parent;
foreach (DirectoryInfo enumerateDirectory in parentDirectoryInfo.SafeEnumerateDirectories())
{
IEnumerable<FileInfo> files = enumerateDirectory.EnumerateFiles().OrderBy(f => f.CreationTimeUtc);
int count = files.Count();
foreach (FileInfo fileInfo in files)
{
try
{
// If the group count is equal to the max count minus 1 then we know we
// have reduced the number of items below the maximum allowed.
// We'll cleanup any orphaned expired files though.
if (!this.IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1)
{
break;
}
// Remove from the cache and delete each CachedImage.
CacheIndexer.Remove(fileInfo.Name);
fileInfo.Delete();
count -= 1;
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
// Do nothing; skip to the next file.
}
}
}
}
return creationDate.AddDays(MaxFileCachedDuration) < DateTime.UtcNow.AddDays(-MaxFileCachedDuration);
}
/// <summary>
/// Gets the full transformed cached path for the image.
/// The images are stored in paths that are based upon the sha1 of their full request path
/// Gets the full transformed cached paths for the image.
/// The images are stored in paths that are based upon the SHA1 of their full request path
/// taking the individual characters of the hash to determine their location.
/// This allows us to store millions of images.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private void GetCachePaths()
{
string streamHash = string.Empty;
@ -286,7 +265,7 @@ namespace ImageProcessor.Web.Caching
{
if (new Uri(this.requestPath).IsFile)
{
// Get the hash for the filestream. That way we can ensure that if the image is
// Get the hash for the filestream. That way we can ensure that if the image is
// updated but has the same name we will know.
FileInfo imageFileInfo = new FileInfo(this.requestPath);
if (imageFileInfo.Exists)
@ -306,8 +285,8 @@ namespace ImageProcessor.Web.Caching
streamHash = string.Empty;
}
// Use an sha1 hash of the full path including the querystring to create the image name.
// That name can also be used as a key for the cached image and we should be able to use
// Use an sha1 hash of the full path including the querystring to create the image name.
// That name can also be used as a key for the cached image and we should be able to use
// The characters of that hash as sub-folders.
string parsedExtension = ImageHelpers.GetExtension(this.fullPath);
string fallbackExtension = this.imageName.Substring(this.imageName.LastIndexOf(".", StringComparison.Ordinal) + 1);
@ -326,22 +305,7 @@ namespace ImageProcessor.Web.Caching
this.virtualCachedPath = Path.Combine(VirtualCachePath, virtualPathFromKey, cachedFileName).Replace(@"\", "/");
}
}
/// <summary>
/// Gets a value indicating whether the given images creation date is out with
/// the prescribed limit.
/// </summary>
/// <param name="creationDate">
/// The creation date.
/// </param>
/// <returns>
/// The true if the date is out with the limit, otherwise; false.
/// </returns>
private bool IsExpired(DateTime creationDate)
{
return creationDate.AddDays(MaxFileCachedDuration) < DateTime.UtcNow.AddDays(-MaxFileCachedDuration);
}
#endregion
#endregion
}
}
}

0
src/ImageProcessor.Web/NET45/Caching/MemCache.cs → src/ImageProcessor.Web/Caching/MemCache.cs

9
src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs → src/ImageProcessor.Web/Configuration/ImageCacheSection.cs

@ -8,18 +8,15 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Config
namespace ImageProcessor.Web.Configuration
{
#region Using
using System.Configuration;
using System.IO;
using System.Xml;
using ImageProcessor.Extensions;
using ImageProcessor.Web.Extensions;
using ImageProcessor.Web.Helpers;
#endregion
/// <summary>
/// Represents an image cache section within a configuration file.
/// </summary>
@ -79,7 +76,7 @@ namespace ImageProcessor.Web.Config
return imageCacheSection;
}
string section = ResourceHelpers.ResourceAsString("ImageProcessor.Web.Config.Resources.cache.config");
string section = ResourceHelpers.ResourceAsString("ImageProcessor.Web.Configuration.Resources.cache.config");
XmlReader reader = new XmlTextReader(new StringReader(section));
imageCacheSection = new ImageCacheSection();
imageCacheSection.DeserializeSection(reader);

8
src/ImageProcessor.Web/NET45/Config/ImageProcessingSection.cs → src/ImageProcessor.Web/Configuration/ImageProcessingSection.cs

@ -5,11 +5,11 @@
// </copyright>
// <summary>
// Represents an image processing section within a configuration file.
// Nested syntax adapted from <see cref="http://tneustaedter.blogspot.co.uk/2011/09/how-to-create-one-or-more-nested.html" />
// Nested syntax adapted from <see href="http://tneustaedter.blogspot.co.uk/2011/09/how-to-create-one-or-more-nested.html" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Config
namespace ImageProcessor.Web.Configuration
{
#region Using
using System.Configuration;
@ -21,7 +21,7 @@ namespace ImageProcessor.Web.Config
/// <summary>
/// Represents an image processing section within a configuration file.
/// Nested syntax adapted from <see cref="http://tneustaedter.blogspot.co.uk/2011/09/how-to-create-one-or-more-nested.html"/>
/// Nested syntax adapted from <see href="http://tneustaedter.blogspot.co.uk/2011/09/how-to-create-one-or-more-nested.html"/>
/// </summary>
public sealed class ImageProcessingSection : ConfigurationSection
{
@ -83,7 +83,7 @@ namespace ImageProcessor.Web.Config
return imageProcessingSection;
}
string section = ResourceHelpers.ResourceAsString("ImageProcessor.Web.Config.Resources.processing.config");
string section = ResourceHelpers.ResourceAsString("ImageProcessor.Web.Configuration.Resources.processing.config");
XmlReader reader = new XmlTextReader(new StringReader(section));
imageProcessingSection = new ImageProcessingSection();
imageProcessingSection.DeserializeSection(reader);

54
src/ImageProcessor.Web/NET45/Config/ImageProcessorConfig.cs → src/ImageProcessor.Web/Configuration/ImageProcessorConfiguration.cs

@ -1,38 +1,39 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageProcessorConfig.cs" company="James South">
// <copyright file="ImageProcessorConfiguration.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to allow the retrieval of ImageProcessor settings.
// <see cref="http://csharpindepth.com/Articles/General/Singleton.aspx" />
// <see href="http://csharpindepth.com/Articles/General/Singleton.aspx" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Config
namespace ImageProcessor.Web.Configuration
{
#region Using
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Compilation;
using ImageProcessor.Common.Extensions;
using ImageProcessor.Processors;
#endregion
using ImageProcessor.Web.Processors;
/// <summary>
/// Encapsulates methods to allow the retrieval of ImageProcessor settings.
/// <see cref="http://csharpindepth.com/Articles/General/Singleton.aspx"/>
/// <see href="http://csharpindepth.com/Articles/General/Singleton.aspx"/>
/// </summary>
public sealed class ImageProcessorConfig
public sealed class ImageProcessorConfiguration
{
#region Fields
/// <summary>
/// A new instance Initializes a new instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class.
/// with lazy initialization.
/// </summary>
private static readonly Lazy<ImageProcessorConfig> Lazy =
new Lazy<ImageProcessorConfig>(() => new ImageProcessorConfig());
private static readonly Lazy<ImageProcessorConfiguration> Lazy =
new Lazy<ImageProcessorConfiguration>(() => new ImageProcessorConfiguration());
/// <summary>
/// A collection of the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElementCollection"/> elements
@ -64,10 +65,11 @@ namespace ImageProcessor.Web.Config
#endregion
#region Constructors
/// <summary>
/// Prevents a default instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class from being created.
/// Prevents a default instance of the <see cref="ImageProcessorConfiguration"/> class from being created.
/// </summary>
private ImageProcessorConfig()
private ImageProcessorConfiguration()
{
this.LoadGraphicsProcessors();
}
@ -75,9 +77,9 @@ namespace ImageProcessor.Web.Config
#region Properties
/// <summary>
/// Gets the current instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class.
/// Gets the current instance of the <see cref="ImageProcessorConfiguration"/> class.
/// </summary>
public static ImageProcessorConfig Instance
public static ImageProcessorConfiguration Instance
{
get
{
@ -88,7 +90,7 @@ namespace ImageProcessor.Web.Config
/// <summary>
/// Gets the list of available GraphicsProcessors.
/// </summary>
public IList<IGraphicsProcessor> GraphicsProcessors { get; private set; }
public IList<IWebGraphicsProcessor> GraphicsProcessors { get; private set; }
/// <summary>
/// Gets a value indicating whether to preserve exif meta data.
@ -292,23 +294,23 @@ namespace ImageProcessor.Web.Config
{
if (GetImageProcessingSection().Plugins.AutoLoadPlugins)
{
Type type = typeof(IGraphicsProcessor);
Type type = typeof(IWebGraphicsProcessor);
try
{
// Build a list of native IGraphicsProcessor instances.
List<Type> availableTypes = BuildManager.GetReferencedAssemblies()
.Cast<Assembly>()
.SelectMany(s => s.GetTypes())
.Where(t => t != null && type.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract)
.SelectMany(s => s.GetLoadableTypes())
.Where(t => type.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract)
.ToList();
// Create them and add.
this.GraphicsProcessors = availableTypes.Select(x => (Activator.CreateInstance(x) as IGraphicsProcessor)).ToList();
this.GraphicsProcessors = availableTypes.Select(x => (Activator.CreateInstance(x) as IWebGraphicsProcessor)).ToList();
// Add the available settings.
foreach (IGraphicsProcessor processor in this.GraphicsProcessors)
foreach (IWebGraphicsProcessor webProcessor in this.GraphicsProcessors)
{
processor.Settings = this.GetPluginSettings(processor.GetType().Name);
webProcessor.Processor.Settings = this.GetPluginSettings(webProcessor.GetType().Name);
}
}
catch (ReflectionTypeLoadException)
@ -332,25 +334,25 @@ namespace ImageProcessor.Web.Config
private void LoadGraphicsProcessorsFromConfiguration()
{
ImageProcessingSection.PluginElementCollection pluginConfigs = imageProcessingSection.Plugins;
this.GraphicsProcessors = new List<IGraphicsProcessor>();
this.GraphicsProcessors = new List<IWebGraphicsProcessor>();
foreach (ImageProcessingSection.PluginElement pluginConfig in pluginConfigs)
{
Type type = Type.GetType(pluginConfig.Type);
if (type == null)
{
throw new TypeLoadException("Couldn't load IGraphicsProcessor: " + pluginConfig.Type);
throw new TypeLoadException("Couldn't load IWebGraphicsProcessor: " + pluginConfig.Type);
}
this.GraphicsProcessors.Add(Activator.CreateInstance(type) as IGraphicsProcessor);
this.GraphicsProcessors.Add(Activator.CreateInstance(type) as IWebGraphicsProcessor);
}
// Add the available settings.
foreach (IGraphicsProcessor processor in this.GraphicsProcessors)
foreach (IWebGraphicsProcessor webProcessor in this.GraphicsProcessors)
{
processor.Settings = this.GetPluginSettings(processor.GetType().Name);
webProcessor.Processor.Settings = this.GetPluginSettings(webProcessor.GetType().Name);
}
}
#endregion
}
}
}

4
src/ImageProcessor.Web/NET45/Config/ImageSecuritySection.cs → src/ImageProcessor.Web/Configuration/ImageSecuritySection.cs

@ -8,7 +8,7 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Config
namespace ImageProcessor.Web.Configuration
{
#region Using
using System;
@ -116,7 +116,7 @@ namespace ImageProcessor.Web.Config
return imageSecuritySection;
}
string section = ResourceHelpers.ResourceAsString("ImageProcessor.Web.Config.Resources.security.config");
string section = ResourceHelpers.ResourceAsString("ImageProcessor.Web.Configuration.Resources.security.config");
XmlReader reader = new XmlTextReader(new StringReader(section));
imageSecuritySection = new ImageSecuritySection();
imageSecuritySection.DeserializeSection(reader);

1
src/ImageProcessor.Web/Configuration/Resources/cache.config

@ -0,0 +1 @@
<cache virtualPath="~/app_data/cache" maxDays="365"/>

43
src/ImageProcessor.Web/Configuration/Resources/processing.config

@ -0,0 +1,43 @@
<processing preserveExifMetaData="false">
<presets>
</presets>
<plugins autoLoadPlugins="true">
<plugin name="Alpha" type="ImageProcessor.Web.Processors.Alpha, ImageProcessor.Web"/>
<plugin name="AutoRotate" type="ImageProcessor.Web.Processors.AutoRotate, ImageProcessor.Web"/>
<plugin name="BackgroundColor" type="ImageProcessor.Web.Processors.BackgroundColor, ImageProcessor.Web"/>
<plugin name="Brightness" type="ImageProcessor.Web.Processors.Brightness, ImageProcessor.Web"/>
<plugin name="Contrast" type="ImageProcessor.Web.Processors.Contrast, ImageProcessor.Web"/>
<plugin name="Crop" type="ImageProcessor.Web.Processors.Crop, ImageProcessor.Web"/>
<plugin name="Filter" type="ImageProcessor.Web.Processors.Filter, ImageProcessor.Web"/>
<plugin name="Flip" type="ImageProcessor.Web.Processors.Flip, ImageProcessor.Web"/>
<plugin name="Format" type="ImageProcessor.Web.Processors.Format, ImageProcessor.Web"/>
<plugin name="GaussianBlur" type="ImageProcessor.Web.Processors.GaussianBlur, ImageProcessor.Web">
<settings>
<setting key="MaxSize" value="22"/>
<setting key="MaxSigma" value="5.1"/>
<setting key="MaxThreshold" value="100"/>
</settings>
</plugin>
<plugin name="GaussianSharpen" type="ImageProcessor.Web.Processors.GaussianSharpen, ImageProcessor.Web">
<settings>
<setting key="MaxSize" value="22"/>
<setting key="MaxSigma" value="5.1"/>
<setting key="MaxThreshold" value="100"/>
</settings>
</plugin>
<plugin name="Meta" type="ImageProcessor.Web.Processors.Meta, ImageProcessor.Web"/>
<plugin name="Quality" type="ImageProcessor.Web.Processors.Quality, ImageProcessor.Web"/>
<plugin name="Resize" type="ImageProcessor.Web.Processors.Resize, ImageProcessor.Web">
<settings>
<setting key="MaxWidth" value="5000"/>
<setting key="MaxHeight" value="5000"/>
</settings>
</plugin>
<plugin name="Rotate" type="ImageProcessor.Web.Processors.Rotate, ImageProcessor.Web"/>
<plugin name="RoundedCorners" type="ImageProcessor.Web.Processors.RoundedCorners, ImageProcessor.Web"/>
<plugin name="Saturation" type="ImageProcessor.Web.Processors.Saturation, ImageProcessor.Web"/>
<plugin name="Tint" type="ImageProcessor.Web.Processors.Tint, ImageProcessor.Web"/>
<plugin name="Vignette" type="ImageProcessor.Web.Processors.Vignette, ImageProcessor.Web"/>
<plugin name="Watermark" type="ImageProcessor.Web.Processors.Watermark, ImageProcessor.Web"/>
</plugins>
</processing>

0
src/ImageProcessor.Web/NET45/Config/Resources/security.config → src/ImageProcessor.Web/Configuration/Resources/security.config

2
src/ImageProcessor.Web/NET45/Extensions/DirectoryInfoExtensions.cs → src/ImageProcessor.Web/Extensions/DirectoryInfoExtensions.cs

@ -53,4 +53,4 @@ namespace ImageProcessor.Web.Extensions
return directories;
}
}
}
}

48
src/ImageProcessor/Extensions/StringExtensions.cs → src/ImageProcessor.Web/Extensions/StringExtensions.cs

@ -8,16 +8,14 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Extensions
namespace ImageProcessor.Web.Extensions
{
#region Using
using System;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
#endregion
/// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.String"/> class.
@ -66,50 +64,8 @@ namespace ImageProcessor.Extensions
.ToString().ToLowerInvariant();
}
}
/// <summary>
/// Creates an SHA256 fingerprint of the String.
/// </summary>
/// <param name="expression">The <see cref="T:System.String">String</see> instance that this method extends.</param>
/// <returns>An SHA256 fingerprint of the String.</returns>
public static string ToSHA256Fingerprint(this string expression)
{
byte[] bytes = Encoding.ASCII.GetBytes(expression.ToCharArray());
using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider())
{
byte[] hash = sha256.ComputeHash(bytes);
// Concatenate the hash bytes into one long String.
return hash.Aggregate(
new StringBuilder(64),
(sb, b) => sb.Append(b.ToString("X2", CultureInfo.InvariantCulture)))
.ToString().ToLowerInvariant();
}
}
/// <summary>
/// Creates an SHA512 fingerprint of the String.
/// </summary>
/// <param name="expression">The <see cref="T:System.String">String</see> instance that this method extends.</param>
/// <returns>An SHA256 fingerprint of the String.</returns>
public static string ToSHA512Fingerprint(this string expression)
{
byte[] bytes = Encoding.ASCII.GetBytes(expression.ToCharArray());
using (SHA512CryptoServiceProvider sha512 = new SHA512CryptoServiceProvider())
{
byte[] hash = sha512.ComputeHash(bytes);
// Concatenate the hash bytes into one long String.
return hash.Aggregate(
new StringBuilder(70),
(sb, b) => sb.Append(b.ToString("X2", CultureInfo.InvariantCulture)))
.ToString().ToLowerInvariant();
}
}
#endregion
#region Numbers
/// <summary>
/// Creates an array of integers scraped from the String.

134
src/ImageProcessor.Web/Helpers/AsyncDuplicateLock.cs

@ -0,0 +1,134 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AsyncDuplicateLock.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Throttles duplicate requests.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Throttles duplicate requests.
/// Based loosely on <see href="http://stackoverflow.com/a/21011273/427899"/>
/// </summary>
public sealed class AsyncDuplicateLock
{
/// <summary>
/// The collection of semaphore slims.
/// </summary>
private static readonly ConcurrentDictionary<object, SemaphoreSlim> SemaphoreSlims
= new ConcurrentDictionary<object, SemaphoreSlim>();
/// <summary>
/// Locks against the given key.
/// </summary>
/// <param name="key">
/// The key that identifies the current object.
/// </param>
/// <returns>
/// The disposable <see cref="Task"/>.
/// </returns>
public IDisposable Lock(object 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;
}
/// <summary>
/// Asynchronously locks against the given key.
/// </summary>
/// <param name="key">
/// The key that identifies the current object.
/// </param>
/// <returns>
/// The disposable <see cref="Task"/>.
/// </returns>
public Task<IDisposable> LockAsync(object key)
{
DisposableScope releaser = new DisposableScope(
key,
s =>
{
SemaphoreSlim locker;
if (SemaphoreSlims.TryRemove(s, out locker))
{
locker.Release();
locker.Dispose();
}
});
Task<IDisposable> 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);
}
/// <summary>
/// The disposable scope.
/// </summary>
private sealed class DisposableScope : IDisposable
{
/// <summary>
/// The key
/// </summary>
private readonly object key;
/// <summary>
/// The close scope action.
/// </summary>
private readonly Action<object> closeScopeAction;
/// <summary>
/// Initializes a new instance of the <see cref="DisposableScope"/> class.
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <param name="closeScopeAction">
/// The close scope action.
/// </param>
public DisposableScope(object key, Action<object> closeScopeAction)
{
this.key = key;
this.closeScopeAction = closeScopeAction;
}
/// <summary>
/// The dispose.
/// </summary>
public void Dispose()
{
this.closeScopeAction(this.key);
}
}
}
}

268
src/ImageProcessor.Web/Helpers/CommonParameterParserUtility.cs

@ -0,0 +1,268 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CommonParameterParserUtility.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to correctly parse querystring parameters.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
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;
using ImageProcessor.Imaging;
using ImageProcessor.Web.Extensions;
/// <summary>
/// Encapsulates methods to correctly parse querystring parameters.
/// </summary>
public static class CommonParameterParserUtility
{
/// <summary>
/// The collection of known colors.
/// </summary>
private static readonly Dictionary<string, KnownColor> KnownColors = new Dictionary<string, KnownColor>();
/// <summary>
/// The regular expression to search strings for colors.
/// </summary>
private static readonly Regex ColorRegex = BuildColorRegex();
/// <summary>
/// The regular expression to search strings for angles.
/// </summary>
private static readonly Regex AngleRegex = new Regex(@"(rotate|angle)(=|-)(?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for values between 1 and 100.
/// </summary>
private static readonly Regex In100RangeRegex = new Regex(@"(-?(0*(?:[1-9][0-9]?|100)))", RegexOptions.Compiled);
/// <summary>
/// The sharpen regex.
/// </summary>
private static readonly Regex BlurSharpenRegex = new Regex(@"(blur|sharpen)=\d+", RegexOptions.Compiled);
/// <summary>
/// The sigma regex.
/// </summary>
private static readonly Regex SigmaRegex = new Regex(@"sigma(=|-)\d+(.?\d+)?", RegexOptions.Compiled);
/// <summary>
/// The threshold regex.
/// </summary>
private static readonly Regex ThresholdRegex = new Regex(@"threshold(=|-)\d+", RegexOptions.Compiled);
/// <summary>
/// Returns the correct <see cref="T:System.Int32"/> containing the angle for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Int32"/> containing the angle for the given string.
/// </returns>
public static int ParseAngle(string input)
{
foreach (Match match in AngleRegex.Matches(input))
{
// Split on angle
int angle;
string value = match.Value.Split(new[] { '=', '-' })[1];
int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out angle);
return angle;
}
// No rotate - matches the RotateLayer default.
return 0;
}
/// <summary>
/// Returns the correct <see cref="T:System.Drawing.Color"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Drawing.Color"/>
/// </returns>
public static Color ParseColor(string input)
{
foreach (Match match in ColorRegex.Matches(input))
{
string value = match.Value.Split(new[] { '=', '-' })[1];
if (KnownColors.ContainsKey(value))
{
return Color.FromKnownColor(KnownColors[value]);
}
if (value.Contains(","))
{
int[] split = value.ToPositiveIntegerArray();
byte red = split[0].ToByte();
byte green = split[1].ToByte();
byte blue = split[2].ToByte();
byte alpha = split[3].ToByte();
return Color.FromArgb(alpha, red, green, blue);
}
// Split on color-hex
return ColorTranslator.FromHtml("#" + value);
}
return Color.Transparent;
}
/// <summary>
/// Returns the correct <see cref="T:System.Int32"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Int32"/> between -100 and 100.
/// </returns>
public static int ParseIn100Range(string input)
{
int value = 0;
foreach (Match match in In100RangeRegex.Matches(input))
{
value = int.Parse(match.Value, CultureInfo.InvariantCulture);
}
return value;
}
/// <summary>
/// Returns the correct <see cref="GaussianLayer"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <param name="maxSize">
/// The maximum size to set the Gaussian kernel to.
/// </param>
/// <param name="maxSigma">
/// The maximum Sigma value (standard deviation) for Gaussian function used to calculate the kernel.
/// </param>
/// <param name="maxThreshold">
/// The maximum threshold value, which is added to each weighted sum of pixels.
/// </param>
/// <returns>
/// The correct <see cref="GaussianLayer"/> .
/// </returns>
public static GaussianLayer ParseGaussianLayer(string input, int maxSize, double maxSigma, int maxThreshold)
{
int size = ParseBlurSharpen(input);
double sigma = ParseSigma(input);
int threshold = ParseThreshold(input);
size = maxSize < size ? maxSize : size;
sigma = maxSigma < sigma ? maxSigma : sigma;
threshold = maxThreshold < threshold ? maxThreshold : threshold;
return new GaussianLayer(size, sigma, threshold);
}
/// <summary>
/// Returns the correct <see cref="T:System.Int32"/> containing the blur value
/// for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Int32"/> for the given string.
/// </returns>
private static int ParseBlurSharpen(string input)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (Match match in BlurSharpenRegex.Matches(input))
{
return Convert.ToInt32(match.Value.Split('=')[1]);
}
return 0;
}
/// <summary>
/// Returns the correct <see cref="T:System.Double"/> containing the sigma value
/// for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Double"/> for the given string.
/// </returns>
private static double ParseSigma(string input)
{
foreach (Match match in SigmaRegex.Matches(input))
{
// split on text-
return Convert.ToDouble(match.Value.Split(new[] { '=', '-' })[1]);
}
return 1.4d;
}
/// <summary>
/// Returns the correct <see cref="T:System.Int32"/> containing the threshold value
/// for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Int32"/> for the given string.
/// </returns>
private static int ParseThreshold(string input)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (Match match in ThresholdRegex.Matches(input))
{
return Convert.ToInt32(match.Value.Split(new[] { '=', '-' })[1]);
}
return 0;
}
/// <summary>
/// Builds a regular expression for the three main colour types.
/// </summary>
/// <returns>
/// The <see cref="Regex"/> to match colors.
/// </returns>
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);
}
}
}

133
src/ImageProcessor.Web/Helpers/ImageHelpers.cs

@ -0,0 +1,133 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageHelpers.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The image helpers.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Configuration;
using ImageProcessor.Imaging.Formats;
/// <summary>
/// The image helpers.
/// </summary>
public static class ImageHelpers
{
/// <summary>
/// The regex pattern.
/// </summary>
private static readonly string ExtensionRegexPattern = BuildExtensionRegexPattern();
/// <summary>
/// The image format regex.
/// </summary>
private static readonly Regex FormatRegex = new Regex(@"(\.?)(png8|" + ExtensionRegexPattern + ")", RegexOptions.IgnoreCase | RegexOptions.RightToLeft);
/// <summary>
/// The image format regex for matching the file format at the end of a string.
/// </summary>
private static readonly Regex EndFormatRegex = new Regex(@"(\.)" + ExtensionRegexPattern + "$", RegexOptions.IgnoreCase | RegexOptions.RightToLeft);
/// <summary>
/// Checks a given string to check whether the value contains a valid image extension.
/// </summary>
/// <param name="fileName">The string containing the filename to check.</param>
/// <returns>True the value contains a valid image extension, otherwise false.</returns>
public static bool IsValidImageExtension(string fileName)
{
return EndFormatRegex.IsMatch(fileName);
}
/// <summary>
/// Returns the correct file extension for the given string input
/// </summary>
/// <param name="input">
/// The string to parse.
/// </param>
/// <returns>
/// The correct file extension for the given string input if it can find one; otherwise an empty string.
/// </returns>
public static string GetExtension(string input)
{
Match match = FormatRegex.Match(input);
if (match.Success)
{
// Ah the enigma that is the png file.
if (match.Value.ToLowerInvariant().EndsWith("png8"))
{
return "png";
}
return match.Value;
}
return string.Empty;
}
/// <summary>
/// Get the correct mime-type for the given string input.
/// </summary>
/// <param name="identifier">
/// The identifier.
/// </param>
/// <returns>
/// The <see cref="string"/> matching the correct mime-type.
/// </returns>
public static string GetMimeType(string identifier)
{
identifier = GetExtension(identifier).Replace(".", string.Empty);
List<ISupportedImageFormat> formats = ImageProcessorBootstrapper.Instance.SupportedImageFormats.ToList();
ISupportedImageFormat format = formats.FirstOrDefault(f => f.FileExtensions.Any(e => e.Equals(identifier, StringComparison.InvariantCultureIgnoreCase)));
if (format != null)
{
return format.MimeType;
}
return string.Empty;
}
/// <summary>
/// Builds a regular expression from the <see cref="T:ImageProcessor.Imaging.Formats.ISupportedImageFormat"/> type, this allows extensibility.
/// </summary>
/// <returns>
/// The <see cref="Regex"/> to match matrix filters.
/// </returns>
private static string BuildExtensionRegexPattern()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("(");
int counter = 0;
foreach (ISupportedImageFormat imageFormat in ImageProcessorBootstrapper.Instance.SupportedImageFormats)
{
foreach (string fileExtension in imageFormat.FileExtensions)
{
if (counter == 0)
{
stringBuilder.Append(fileExtension.ToLowerInvariant());
}
else
{
stringBuilder.AppendFormat("|{0}", fileExtension.ToLowerInvariant());
}
}
counter++;
}
stringBuilder.Append(")");
return stringBuilder.ToString();
}
}
}

44
src/ImageProcessor.Web/Helpers/NativeMethods.cs

@ -0,0 +1,44 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="NativeMethods.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides access to unmanaged native methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
using System;
using System.Runtime.InteropServices;
/// <summary>
/// Provides access to unmanaged native methods.
/// </summary>
internal class NativeMethods
{
/// <summary>
/// Loads the specified module into the address space of the calling process.
/// The specified module may cause other modules to be loaded.
/// </summary>
/// <param name="libname">
/// The name of the module. This can be either a library module or
/// an executable module.
/// </param>
/// <returns>If the function succeeds, the return value is a handle to the module; otherwise null.</returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibrary(string libname);
/// <summary>
/// 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.
/// </summary>
/// <param name="hModule">A handle to the loaded library module.
/// The LoadLibrary, LoadLibraryEx, GetModuleHandle, or GetModuleHandleEx function returns this handle.</param>
/// <returns>If the function succeeds, the return value is nonzero; otherwise zero.</returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool FreeLibrary(IntPtr hModule);
}
}

25
src/ImageProcessor.Web/Helpers/PostProcessingEventArgs.cs

@ -0,0 +1,25 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PostProcessingEventArgs.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The post processing event arguments.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
using System;
/// <summary>
/// The post processing event arguments.
/// </summary>
public class PostProcessingEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the cached image path.
/// </summary>
public string CachedImagePath { get; set; }
}
}

61
src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs → src/ImageProcessor.Web/Helpers/RemoteFile.cs

@ -13,13 +13,16 @@ namespace ImageProcessor.Web.Helpers
#region Using
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using ImageProcessor.Web.Config;
using System.Web;
using ImageProcessor.Web.Configuration;
#endregion
/// <summary>
@ -40,7 +43,7 @@ namespace ImageProcessor.Web.Helpers
/// There shouldn't be any security issues there, as the internal WebRequest instance is still calling it remotely.
/// Any local files that shouldn't be accessed by this won't be allowed by the remote call.
/// </para>
/// Adapted from <see cref="http://blogengine.codeplex.com">BlogEngine.Net</see>
/// Adapted from <see href="http://blogengine.codeplex.com">BlogEngine.Net</see>
/// </remarks>
internal sealed class RemoteFile
{
@ -48,27 +51,27 @@ namespace ImageProcessor.Web.Helpers
/// <summary>
/// The white-list of url[s] from which to download remote files.
/// </summary>
public static readonly ImageSecuritySection.SafeUrl[] RemoteFileWhiteListExtensions = ImageProcessorConfig.Instance.RemoteFileWhiteListExtensions;
public static readonly ImageSecuritySection.SafeUrl[] RemoteFileWhiteListExtensions = ImageProcessorConfiguration.Instance.RemoteFileWhiteListExtensions;
/// <summary>
/// The white-list of url[s] from which to download remote files.
/// </summary>
private static readonly Uri[] RemoteFileWhiteList = ImageProcessorConfig.Instance.RemoteFileWhiteList;
private static readonly Uri[] RemoteFileWhiteList = ImageProcessorConfiguration.Instance.RemoteFileWhiteList;
/// <summary>
/// The length of time, in milliseconds, that a remote file download attempt can last before timing out.
/// </summary>
private static readonly int TimeoutMilliseconds = ImageProcessorConfig.Instance.Timeout;
private static readonly int TimeoutMilliseconds = ImageProcessorConfiguration.Instance.Timeout;
/// <summary>
/// The maximum size, in bytes, that a remote file download attempt can download.
/// </summary>
private static readonly int MaxBytes = ImageProcessorConfig.Instance.MaxBytes;
private static readonly int MaxBytes = ImageProcessorConfiguration.Instance.MaxBytes;
/// <summary>
/// Whether to allow remote downloads.
/// </summary>
private static readonly bool AllowRemoteDownloads = ImageProcessorConfig.Instance.AllowRemoteDownloads;
private static readonly bool AllowRemoteDownloads = ImageProcessorConfiguration.Instance.AllowRemoteDownloads;
/// <summary>
/// Whether this RemoteFile instance is ignoring remote download rules set in the current application
@ -241,27 +244,43 @@ namespace ImageProcessor.Web.Helpers
/// </returns>
internal async Task<WebResponse> GetWebResponseAsync()
{
WebResponse response = await this.GetWebRequest().GetResponseAsync();
WebResponse response = null;
try
{
response = await this.GetWebRequest().GetResponseAsync();
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.NameResolutionFailure)
{
throw new HttpException(404, "No image exists at " + Uri);
}
long contentLength = response.ContentLength;
throw;
}
// WebResponse.ContentLength doesn't always know the value, it returns -1 in this case.
if (contentLength == -1)
if (response != null)
{
// Response headers may still have the Content-Length inside of it.
string headerContentLength = response.Headers["Content-Length"];
long contentLength = response.ContentLength;
if (!string.IsNullOrWhiteSpace(headerContentLength))
// WebResponse.ContentLength doesn't always know the value, it returns -1 in this case.
if (contentLength == -1)
{
contentLength = long.Parse(headerContentLength, CultureInfo.InvariantCulture);
// Response headers may still have the Content-Length inside of it.
string headerContentLength = response.Headers["Content-Length"];
if (!string.IsNullOrWhiteSpace(headerContentLength))
{
contentLength = long.Parse(headerContentLength, CultureInfo.InvariantCulture);
}
}
}
// We don't need to check the url here since any external urls are available only from the web.config.
if ((this.MaxDownloadSize > 0) && (contentLength > this.MaxDownloadSize))
{
response.Close();
throw new SecurityException("An attempt to download a remote file has been halted because the file is larger than allowed.");
// We don't need to check the url here since any external urls are available only from the web.config.
if ((this.MaxDownloadSize > 0) && (contentLength > this.MaxDownloadSize))
{
response.Close();
throw new SecurityException("An attempt to download a remote file has been halted because the file is larger than allowed.");
}
}
return response;

0
src/ImageProcessor.Web/NET45/Helpers/ResourceHelpers.cs → src/ImageProcessor.Web/Helpers/ResourceHelpers.cs

269
src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs → src/ImageProcessor.Web/HttpModules/ImageProcessingModule.cs

@ -1,4 +1,4 @@
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageProcessingModule.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
@ -12,7 +12,6 @@ namespace ImageProcessor.Web.HttpModules
{
#region Using
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@ -23,14 +22,14 @@ 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;
using System.Web.Security;
using ImageProcessor.Extensions;
using ImageProcessor.Web.Caching;
using ImageProcessor.Web.Config;
using ImageProcessor.Web.Configuration;
using ImageProcessor.Web.Extensions;
using ImageProcessor.Web.Helpers;
#endregion
@ -45,10 +44,20 @@ namespace ImageProcessor.Web.HttpModules
/// </summary>
private const string CachedResponseTypeKey = "CACHED_IMAGE_RESPONSE_TYPE_054F217C-11CF-49FF-8D2F-698E8E6EB58F";
/// <summary>
/// The key for storing the cached path of the current image.
/// </summary>
private const string CachedPathKey = "CACHED_IMAGE_PATH_TYPE_E0741478-C17B-433D-96A8-6CDA797644E9";
/// <summary>
/// The key for storing the file dependency of the current image.
/// </summary>
private const string CachedResponseFileDependency = "CACHED_IMAGE_DEPENDENCY_054F217C-11CF-49FF-8D2F-698E8E6EB58F";
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex PresetRegex = new Regex(@"preset=[^&]*", RegexOptions.Compiled);
private static readonly Regex PresetRegex = new Regex(@"preset=[^&]+", RegexOptions.Compiled);
/// <summary>
/// The assembly version.
@ -56,9 +65,9 @@ namespace ImageProcessor.Web.HttpModules
private static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
/// <summary>
/// The collection of SemaphoreSlims for identifying given locking individual queries.
/// The locker for preventing duplicate requests.
/// </summary>
private static readonly ConcurrentDictionary<string, SemaphoreSlim> SemaphoreSlims = new ConcurrentDictionary<string, SemaphoreSlim>();
private static readonly AsyncDuplicateLock Locker = new AsyncDuplicateLock();
/// <summary>
/// The value to prefix any remote image requests with to ensure they get captured.
@ -86,11 +95,11 @@ namespace ImageProcessor.Web.HttpModules
#region Destructors
/// <summary>
/// Finalizes an instance of the <see cref="T:ImageProcessor.Web.HttpModules.ImageProcessingModule"/> class.
/// Finalizes an instance of the <see cref="T:ImageProcessor.Web.HttpModules.ImageProcessingModule"/> class.
/// </summary>
/// <remarks>
/// Use C# destructor syntax for finalization code.
/// This destructor will run only if the Dispose method
/// This destructor will run only if the Dispose method
/// does not get called.
/// It gives your base class the opportunity to finalize.
/// Do not provide destructors in types derived from this class.
@ -104,33 +113,38 @@ namespace ImageProcessor.Web.HttpModules
}
#endregion
/// <summary>
/// The event that is called when a new image is processed.
/// </summary>
public static event EventHandler<PostProcessingEventArgs> OnPostProcessing;
#region IHttpModule Members
/// <summary>
/// Initializes a module and prepares it to handle requests.
/// </summary>
/// <param name="context">
/// An <see cref="T:System.Web.HttpApplication"/> that provides
/// access to the methods, properties, and events common to all
/// An <see cref="T:System.Web.HttpApplication"/> that provides
/// access to the methods, properties, and events common to all
/// application objects within an ASP.NET application
/// </param>
public void Init(HttpApplication context)
{
if (remotePrefix == null)
{
remotePrefix = ImageProcessorConfig.Instance.RemotePrefix;
remotePrefix = ImageProcessorConfiguration.Instance.RemotePrefix;
}
if (preserveExifMetaData == null)
{
preserveExifMetaData = ImageProcessorConfig.Instance.PreserveExifMetaData;
preserveExifMetaData = ImageProcessorConfiguration.Instance.PreserveExifMetaData;
}
#if NET45
EventHandlerTaskAsyncHelper wrapper = new EventHandlerTaskAsyncHelper(this.PostAuthorizeRequest);
context.AddOnPostAuthorizeRequestAsync(wrapper.BeginEventHandler, wrapper.EndEventHandler);
#else
context.PostAuthorizeRequest += this.PostAuthorizeRequest;
#endif
EventHandlerTaskAsyncHelper postAuthorizeHelper = new EventHandlerTaskAsyncHelper(this.PostAuthorizeRequest);
context.AddOnPostAuthorizeRequestAsync(postAuthorizeHelper.BeginEventHandler, postAuthorizeHelper.EndEventHandler);
EventHandlerTaskAsyncHelper postProcessHelper = new EventHandlerTaskAsyncHelper(this.PostProcessImage);
context.AddOnPostRequestHandlerExecuteAsync(postProcessHelper.BeginEventHandler, postProcessHelper.EndEventHandler);
context.PreSendRequestHeaders += this.ContextPreSendRequestHeaders;
}
@ -143,28 +157,12 @@ namespace ImageProcessor.Web.HttpModules
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets the specific <see cref="T:System.Threading.SemaphoreSlim"/> for the given id.
/// </summary>
/// <param name="id">
/// The id representing the <see cref="T:System.Threading.SemaphoreSlim"/>.
/// </param>
/// <returns>
/// The <see cref="T:System.Threading.Mutex"/> for the given id.
/// </returns>
private static SemaphoreSlim GetSemaphoreSlim(string id)
{
id = id.ToMD5Fingerprint();
SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(id, new SemaphoreSlim(1, 1));
return semaphore;
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
@ -179,12 +177,6 @@ namespace ImageProcessor.Web.HttpModules
if (disposing)
{
// Dispose of any managed resources here.
//foreach (KeyValuePair<string, SemaphoreSlim> semaphore in SemaphoreSlims)
//{
// semaphore.Value.Dispose();
//}
//SemaphoreSlims.Clear();
}
// Call the appropriate methods to clean up
@ -194,8 +186,6 @@ namespace ImageProcessor.Web.HttpModules
}
#endregion
#if NET45
/// <summary>
/// Occurs when the user for the current request has been authorized.
/// </summary>
@ -214,20 +204,41 @@ namespace ImageProcessor.Web.HttpModules
return this.ProcessImageAsync(context);
}
#else
/// <summary>
/// Occurs when the user for the current request has been authorized.
/// Occurs when the ASP.NET event handler finishes execution.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An <see cref="T:System.EventArgs">EventArgs</see> that contains the event data.</param>
private async void PostAuthorizeRequest(object sender, EventArgs e)
/// <param name="sender">
/// The source of the event.
/// </param>
/// <param name="e">
/// An <see cref="T:System.EventArgs">EventArgs</see> that contains the event data.
/// </param>
/// <returns>
/// The <see cref="T:System.Threading.Tasks.Task"/>.
/// </returns>
private Task PostProcessImage(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
await this.ProcessImageAsync(context);
}
object cachedPathObject = context.Items[CachedPathKey];
#endif
if (cachedPathObject != null)
{
string cachedPath = cachedPathObject.ToString();
// Trim the cache.
DiskCache.TrimCachedFolders(cachedPath);
// Fire the post processing event.
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler != null)
{
context.Items[CachedPathKey] = null;
return Task.Run(() => handler(this, new PostProcessingEventArgs { CachedImagePath = cachedPath }));
}
}
return Task.FromResult<object>(null);
}
/// <summary>
/// Occurs just before ASP.NET send HttpHeaders to the client.
@ -239,15 +250,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;
List<string> dependencyFiles = (List<string>)dependencyFileObject;
// Set the headers
this.SetHeaders(context, responseType);
this.SetHeaders(context, responseType, dependencyFiles);
context.Items[CachedResponseTypeKey] = null;
context.Items[CachedResponseFileDependency] = null;
}
}
@ -256,8 +270,8 @@ namespace ImageProcessor.Web.HttpModules
/// Processes the image.
/// </summary>
/// <param name="context">
/// the <see cref="T:System.Web.HttpContext">HttpContext</see> object that provides
/// references to the intrinsic server objects
/// the <see cref="T:System.Web.HttpContext">HttpContext</see> object that provides
/// references to the intrinsic server objects
/// </param>
/// <returns>
/// The <see cref="T:System.Threading.Tasks.Task"/>.
@ -336,7 +350,7 @@ namespace ImageProcessor.Web.HttpModules
{
string hashedUrlParameters = urlParameters.ToMD5Fingerprint();
// TODO: Add hash for querystring parameters.
// TODO: Add hash for querystring parameters?
imageName += hashedUrlParameters;
fullPath += hashedUrlParameters;
}
@ -346,12 +360,13 @@ namespace ImageProcessor.Web.HttpModules
}
// Create a new cache to help process and cache the request.
DiskCache cache = new DiskCache(requestPath, fullPath, imageName, isRemote);
DiskCache cache = new DiskCache(requestPath, fullPath, imageName);
string cachedPath = cache.CachedPath;
// Since we are now rewriting the path we need to check again that the current user has access
// to the rewritten path.
// Get the user for the current request
// If the user is anonymous or authentication doesn't work for this suffix avoid a NullReferenceException
// If the user is anonymous or authentication doesn't work for this suffix avoid a NullReferenceException
// in the UrlAuthorizationModule by creating a generic identity.
string virtualCachedPath = cache.VirtualCachedPath;
@ -361,9 +376,11 @@ namespace ImageProcessor.Web.HttpModules
PermissionSet permission = new PermissionSet(PermissionState.None);
permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted));
bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet);
bool isAllowed = true;
// Run the rewritten path past the auth system again, using the result as the default "AllowAccess" value
// Run the rewritten path past the authorization system again.
// We can then use the result as the default "AllowAccess" value
if (hasPermission && !context.SkipAuthorization)
{
isAllowed = UrlAuthorizationModule.CheckUrlAccessForPrincipal(virtualCachedPath, user, "GET");
@ -372,29 +389,23 @@ namespace ImageProcessor.Web.HttpModules
if (isAllowed)
{
// Is the file new or updated?
bool isNewOrUpdated = await cache.IsNewOrUpdatedFileAsync();
bool isNewOrUpdated = cache.IsNewOrUpdatedFile(cachedPath);
// Only process if the file has been updated.
if (isNewOrUpdated)
{
string cachedPath = cache.CachedPath;
// Process the image.
using (ImageFactory imageFactory = new ImageFactory(preserveExifMetaData != null && preserveExifMetaData.Value))
{
if (isRemote)
{
Uri uri = new Uri(requestPath + "?" + urlParameters);
RemoteFile remoteFile = new RemoteFile(uri, false);
// Prevent response blocking.
WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false);
SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath);
try
using (await Locker.LockAsync(cachedPath))
{
semaphore.Wait();
Uri uri = new Uri(requestPath + "?" + urlParameters);
RemoteFile remoteFile = new RemoteFile(uri, false);
// Prevent response blocking.
WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false);
using (MemoryStream memoryStream = new MemoryStream())
{
@ -406,82 +417,86 @@ namespace ImageProcessor.Web.HttpModules
{
responseStream.CopyTo(memoryStream);
// Reset the position of the stream to ensure we're reading the correct part.
memoryStream.Position = 0;
// Process the Image
imageFactory.Load(memoryStream)
.AddQueryString(queryString)
.AutoProcess()
.AutoProcess(queryString)
.Save(cachedPath);
// Store the response type in the context for later retrieval.
context.Items[CachedResponseTypeKey] = imageFactory.MimeType;
// Add to the cache.
cache.AddImageToCache();
cache.AddImageToCache(cachedPath);
// Trim the cache.
await cache.TrimCachedFolderAsync(cachedPath);
// Store the cached path, response type, and cache dependency in the context for later retrieval.
context.Items[CachedPathKey] = cachedPath;
context.Items[CachedResponseTypeKey] = imageFactory.CurrentImageFormat.MimeType;
context.Items[CachedResponseFileDependency] = new List<string> { cachedPath };
}
}
}
}
}
finally
{
semaphore.Release();
}
}
else
{
// Check to see if the file exists.
// ReSharper disable once AssignNullToNotNullAttribute
FileInfo fileInfo = new FileInfo(requestPath);
if (!fileInfo.Exists)
using (Locker.Lock(cachedPath))
{
throw new HttpException(404, "No image exists at " + fullPath);
}
// Check to see if the file exists.
// ReSharper disable once AssignNullToNotNullAttribute
FileInfo fileInfo = new FileInfo(requestPath);
SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath);
try
{
semaphore.Wait();
if (!fileInfo.Exists)
{
throw new HttpException(404, "No image exists at " + fullPath);
}
// Process the Image
imageFactory.Load(fullPath).AutoProcess().Save(cachedPath);
// Store the response type in the context for later retrieval.
context.Items[CachedResponseTypeKey] = imageFactory.MimeType;
imageFactory.Load(requestPath)
.AutoProcess(queryString)
.Save(cachedPath);
// Add to the cache.
cache.AddImageToCache();
cache.AddImageToCache(cachedPath);
// Trim the cache.
await cache.TrimCachedFolderAsync(cachedPath);
}
finally
{
semaphore.Release();
// Store the cached path, response type, and cache dependencies in the context for later retrieval.
context.Items[CachedPathKey] = cachedPath;
context.Items[CachedResponseTypeKey] = imageFactory.CurrentImageFormat.MimeType;
context.Items[CachedResponseFileDependency] = new List<string> { requestPath, cachedPath };
}
}
}
}
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)
{
string mimetype = ImageHelpers.GetMimeType(cachedPath);
if (!string.IsNullOrEmpty(mimetype))
{
context.Items[CachedResponseTypeKey] = mimetype;
}
}
string incomingEtag = context.Request.Headers["If" + "-None-Match"];
if (incomingEtag != null && !isNewOrUpdated)
{
// Explicitly set the Content-Length header so the client doesn't wait for
// content but keeps the connection open for other requests
// Set the Content-Length header so the client doesn't wait for
// content but keeps the connection open for other requests.
context.Response.AddHeader("Content-Length", "0");
context.Response.StatusCode = (int)HttpStatusCode.NotModified;
context.Response.SuppressContent = true;
context.Response.AddFileDependency(context.Server.MapPath(virtualCachedPath));
this.SetHeaders(context, (string)context.Items[CachedResponseTypeKey]);
if (!isRemote)
{
// Set the headers and quit.
this.SetHeaders(context, (string)context.Items[CachedResponseTypeKey], new List<string> { requestPath, cachedPath });
return;
}
this.SetHeaders(context, (string)context.Items[CachedResponseTypeKey], new List<string> { cachedPath });
}
// The cached file is valid so just rewrite the path.
@ -494,7 +509,7 @@ namespace ImageProcessor.Web.HttpModules
}
else if (isRemote)
{
// Just repoint to the external url.
// Just re-point to the external url.
HttpContext.Current.Response.Redirect(requestPath);
}
}
@ -505,21 +520,31 @@ namespace ImageProcessor.Web.HttpModules
/// See http://en.wikipedia.org/wiki/HTTP_ETag
/// </summary>
/// <param name="context">
/// the <see cref="T:System.Web.HttpContext">HttpContext</see> object that provides
/// references to the intrinsic server objects
/// the <see cref="T:System.Web.HttpContext">HttpContext</see> object that provides
/// references to the intrinsic server objects
/// </param>
/// <param name="responseType">
/// The HTTP MIME type to to send.
/// </param>
/// <param name="responseType">The HTTP MIME type to to send.</param>
private void SetHeaders(HttpContext context, string responseType)
/// <param name="dependencyPaths">
/// The dependency path for the cache dependency.
/// </param>
private void SetHeaders(HttpContext context, string responseType, IEnumerable<string> dependencyPaths)
{
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.AddFileDependencies(dependencyPaths.ToArray());
cache.SetLastModifiedFromFileDependencies();
int maxDays = DiskCache.MaxFileCachedDuration;
@ -547,7 +572,7 @@ namespace ImageProcessor.Web.HttpModules
string preset = match.Value.Split('=')[1];
// We use the processor config system to store the preset values.
string replacements = ImageProcessorConfig.Instance.GetPresetSettings(preset);
string replacements = ImageProcessorConfiguration.Instance.GetPresetSettings(preset);
queryString = Regex.Replace(queryString, preset, replacements ?? string.Empty);
}
}
@ -556,4 +581,4 @@ namespace ImageProcessor.Web.HttpModules
}
#endregion
}
}
}

66
src/ImageProcessor.Web/ImageFactoryExtensions.cs

@ -0,0 +1,66 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageFactoryExtensions.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Extends the ImageFactory class to provide a fluent API.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web
{
#region Using
using System.Collections.Generic;
using System.Linq;
using ImageProcessor.Web.Configuration;
using ImageProcessor.Web.Processors;
#endregion
/// <summary>
/// Extends the ImageFactory class to provide a fluent API.
/// </summary>
public static class ImageFactoryExtensions
{
/// <summary>
/// The object to lock against.
/// </summary>
private static readonly object SyncRoot = new object();
/// <summary>
/// Auto processes image files based on any query string parameters added to the image path.
/// </summary>
/// <param name="factory">
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class
/// that this method extends.
/// </param>
/// <param name="queryString">The collection of querystring parameters to process.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public static ImageFactory AutoProcess(this ImageFactory factory, string queryString)
{
if (factory.ShouldProcess)
{
// It's faster to lock and run through our activated list than to create new instances.
lock (SyncRoot)
{
// Get a list of all graphics processors that have parsed and matched the query string.
List<IWebGraphicsProcessor> graphicsProcessors =
ImageProcessorConfiguration.Instance.GraphicsProcessors
.Where(x => x.MatchRegexIndex(queryString) != int.MaxValue)
.OrderBy(y => y.SortOrder)
.ToList();
// Loop through and process the image.
foreach (IWebGraphicsProcessor graphicsProcessor in graphicsProcessors)
{
factory.CurrentImageFormat.ApplyProcessor(graphicsProcessor.Processor.ProcessImage, factory);
}
}
}
return factory;
}
}
}

43
src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj → src/ImageProcessor.Web/ImageProcessor.Web.csproj

@ -49,33 +49,58 @@
<Compile Include="Caching\MemCache.cs" />
<Compile Include="Caching\DiskCache.cs" />
<Compile Include="Caching\CacheIndexer.cs" />
<Compile Include="Config\ImageCacheSection.cs" />
<Compile Include="Config\ImageProcessingSection.cs" />
<Compile Include="Config\ImageProcessorConfig.cs" />
<Compile Include="Config\ImageSecuritySection.cs" />
<Compile Include="Configuration\ImageCacheSection.cs" />
<Compile Include="Configuration\ImageProcessingSection.cs" />
<Compile Include="Configuration\ImageProcessorConfiguration.cs" />
<Compile Include="Configuration\ImageSecuritySection.cs" />
<Compile Include="Extensions\DirectoryInfoExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Helpers\AsyncDuplicateLock.cs" />
<Compile Include="Helpers\CommonParameterParserUtility.cs" />
<Compile Include="Helpers\NativeMethods.cs" />
<Compile Include="Helpers\PostProcessingEventArgs.cs" />
<Compile Include="Helpers\ResourceHelpers.cs" />
<Compile Include="Helpers\ImageHelpers.cs" />
<Compile Include="Helpers\RemoteFile.cs" />
<Compile Include="Helpers\TaskHelpers.cs" />
<Compile Include="HttpModules\ImageProcessingModule.cs" />
<Compile Include="ImageFactoryExtensions.cs" />
<Compile Include="Processors\Alpha.cs" />
<Compile Include="Processors\AutoRotate.cs" />
<Compile Include="Processors\BackgroundColor.cs" />
<Compile Include="Processors\Brightness.cs" />
<Compile Include="Processors\Contrast.cs" />
<Compile Include="Processors\Crop.cs" />
<Compile Include="Processors\Filter.cs" />
<Compile Include="Processors\Flip.cs" />
<Compile Include="Processors\Format.cs" />
<Compile Include="Processors\GaussianSharpen.cs" />
<Compile Include="Processors\GaussianBlur.cs" />
<Compile Include="Processors\IWebGraphicsProcessor.cs" />
<Compile Include="Processors\Quality.cs" />
<Compile Include="Processors\Resize.cs" />
<Compile Include="Processors\Rotate.cs" />
<Compile Include="Processors\RoundedCorners.cs" />
<Compile Include="Processors\Saturation.cs" />
<Compile Include="Processors\Tint.cs" />
<Compile Include="Processors\Meta.cs" />
<Compile Include="Processors\Vignette.cs" />
<Compile Include="Processors\Watermark.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ImageProcessor\ImageProcessor.csproj">
<ProjectReference Include="..\ImageProcessor\ImageProcessor.csproj">
<Project>{3b5dd734-fb7a-487d-8ce6-55e7af9aea7e}</Project>
<Name>ImageProcessor</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Config\Resources\cache.config">
<EmbeddedResource Include="Configuration\Resources\cache.config">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Config\Resources\processing.config">
<EmbeddedResource Include="Configuration\Resources\processing.config">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Config\Resources\security.config" />
<EmbeddedResource Include="Configuration\Resources\security.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />

146
src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj

@ -1,146 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4F7050F2-465F-4E10-8DB2-2FB97AC6AA43}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ImageProcessor.Web</RootNamespace>
<AssemblyName>ImageProcessor.Web</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;USE_CSHARP_SQLITE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;USE_CSHARP_SQLITE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'All|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\All\</OutputPath>
<DefineConstants>TRACE;DEBUG;USE_CSHARP_SQLITE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO">
<HintPath>..\..\packages\Microsoft.Bcl.1.1.9\lib\net40\System.IO.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Runtime">
<HintPath>..\..\packages\Microsoft.Bcl.1.1.9\lib\net40\System.Runtime.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Threading.Tasks">
<HintPath>..\..\packages\Microsoft.Bcl.1.1.9\lib\net40\System.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Data" />
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\NET45\Caching\CachedImage.cs" />
<Compile Include="..\NET45\Caching\CacheIndexer.cs">
<Link>CacheIndexer.cs</Link>
</Compile>
<Compile Include="..\NET45\Caching\DiskCache.cs" />
<Compile Include="..\NET45\Caching\MemCache.cs">
<Link>MemCache.cs</Link>
</Compile>
<Compile Include="..\NET45\Config\ImageCacheSection.cs" />
<Compile Include="..\NET45\Config\ImageProcessingSection.cs" />
<Compile Include="..\NET45\Config\ImageProcessorConfig.cs" />
<Compile Include="..\NET45\Config\ImageSecuritySection.cs" />
<Compile Include="..\NET45\Extensions\DirectoryInfoExtensions.cs">
<Link>DirectoryInfoExtensions.cs</Link>
</Compile>
<Compile Include="..\NET45\Helpers\ImageHelpers.cs">
<Link>ImageHelpers.cs</Link>
</Compile>
<Compile Include="..\NET45\Helpers\RemoteFile.cs" />
<Compile Include="..\NET45\Helpers\ResourceHelpers.cs">
<Link>ResourceHelpers.cs</Link>
</Compile>
<Compile Include="..\NET45\Helpers\TaskHelpers.cs" />
<Compile Include="..\NET45\HttpModules\ImageProcessingModule.cs" />
<Compile Include="..\NET45\ImageFactoryExtensions.cs" />
<Compile Include="..\NET45\Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ImageProcessor\ImageProcessor.csproj">
<Project>{3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E}</Project>
<Name>ImageProcessor</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\NET45\Config\Resources\cache.config">
<Link>Config\Resources\cache.config</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\NET45\Config\Resources\processing.config">
<Link>Config\Resources\processing.config</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\NET45\Config\Resources\security.config">
<Link>Config\Resources\security.config</Link>
</EmbeddedResource>
<None Include="app.config">
<SubType>Designer</SubType>
</None>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

11
src/ImageProcessor.Web/NET4/Settings.StyleCop

@ -1,11 +0,0 @@
<StyleCopSettings Version="105">
<Analyzers>
<Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
<AnalyzerSettings>
<StringProperty Name="CompanyName">James South</StringProperty>
<StringProperty Name="Copyright">Copyright (c) James South.
Licensed under the Apache License, Version 2.0.</StringProperty>
</AnalyzerSettings>
</Analyzer>
</Analyzers>
</StyleCopSettings>

15
src/ImageProcessor.Web/NET4/app.config

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns:bcl="urn:schemas-microsoft-com:bcl" xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly bcl:name="System.Runtime">
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.9.0" newVersion="2.6.9.0" />
</dependentAssembly>
<dependentAssembly bcl:name="System.Threading.Tasks">
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.9.0" newVersion="2.6.9.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

6
src/ImageProcessor.Web/NET4/packages.config

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="net40" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net40" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net40" />
</packages>

1
src/ImageProcessor.Web/NET45/Config/Resources/cache.config

@ -1 +0,0 @@
<cache virtualPath="~/app_data/cache" maxDays="356"/>

41
src/ImageProcessor.Web/NET45/Config/Resources/processing.config

@ -1,41 +0,0 @@
<processing preserveExifMetaData="false">
<presets>
</presets>
<plugins autoLoadPlugins="true">
<plugin name="Alpha" type="ImageProcessor.Processors.Alpha, ImageProcessor"/>
<plugin name="AutoRotate" type="ImageProcessor.Processors.AutoRotate, ImageProcessor"/>
<plugin name="Brightness" type="ImageProcessor.Processors.Brightness, ImageProcessor"/>
<plugin name="Contrast" type="ImageProcessor.Processors.Contrast, ImageProcessor"/>
<plugin name="Crop" type="ImageProcessor.Processors.Crop, ImageProcessor"/>
<plugin name="Filter" type="ImageProcessor.Processors.Filter, ImageProcessor"/>
<plugin name="Flip" type="ImageProcessor.Processors.Flip, ImageProcessor"/>
<plugin name="Format" type="ImageProcessor.Processors.Format, ImageProcessor"/>
<plugin name="GaussianBlur" type="ImageProcessor.Processors.GaussianBlur, ImageProcessor">
<settings>
<setting key="MaxSize" value="22"/>
<setting key="MaxSigma" value="5.1"/>
<setting key="MaxThreshold" value="100"/>
</settings>
</plugin>
<plugin name="GaussianSharpen" type="ImageProcessor.Processors.GaussianSharpen, ImageProcessor">
<settings>
<setting key="MaxSize" value="22"/>
<setting key="MaxSigma" value="5.1"/>
<setting key="MaxThreshold" value="100"/>
</settings>
</plugin>
<plugin name="Quality" type="ImageProcessor.Processors.Quality, ImageProcessor"/>
<plugin name="Resize" type="ImageProcessor.Processors.Resize, ImageProcessor">
<settings>
<setting key="MaxWidth" value="5000"/>
<setting key="MaxHeight" value="5000"/>
</settings>
</plugin>
<plugin name="Rotate" type="ImageProcessor.Processors.Rotate, ImageProcessor"/>
<plugin name="RoundedCorners" type="ImageProcessor.Processors.RoundedCorners, ImageProcessor"/>
<plugin name="Saturation" type="ImageProcessor.Processors.Saturation, ImageProcessor"/>
<plugin name="Tint" type="ImageProcessor.Processors.Tint, ImageProcessor"/>
<plugin name="Vignette" type="ImageProcessor.Processors.Vignette, ImageProcessor"/>
<plugin name="Watermark" type="ImageProcessor.Processors.Watermark, ImageProcessor"/>
</plugins>
</processing>

57
src/ImageProcessor.Web/NET45/Helpers/ImageHelpers.cs

@ -1,57 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageHelpers.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The image helpers.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
#region Using
using System.Text.RegularExpressions;
#endregion
/// <summary>
/// The image helpers.
/// </summary>
public static class ImageHelpers
{
/// <summary>
/// The image format regex.
/// </summary>
private static readonly Regex FormatRegex = new Regex(@"(\.?)(j(pg|peg)|bmp|png|gif|ti(ff|f)|ico)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.RightToLeft);
/// <summary>
/// The image format regex for matching the file format at the end of a string.
/// </summary>
private static readonly Regex EndFormatRegex = new Regex(@"(\.)(j(pg|peg)|bmp|png|gif|ti(ff|f)|ico)$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.RightToLeft);
/// <summary>
/// Checks a given string to check whether the value contains a valid image extension.
/// </summary>
/// <param name="fileName">The string containing the filename to check.</param>
/// <returns>True the value contains a valid image extension, otherwise false.</returns>
public static bool IsValidImageExtension(string fileName)
{
return EndFormatRegex.IsMatch(fileName);
}
/// <summary>
/// Returns the correct file extension for the given string input
/// </summary>
/// <param name="input">
/// The string to parse.
/// </param>
/// <returns>
/// The correct file extension for the given string input if it can find one; otherwise an empty string.
/// </returns>
public static string GetExtension(string input)
{
Match match = FormatRegex.Matches(input)[0];
return match.Success ? match.Value : string.Empty;
}
}
}

51
src/ImageProcessor.Web/NET45/Helpers/TaskHelpers.cs

@ -1,51 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TaskHelpers.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides some syntactic sugar to run tasks.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Helpers
{
#region Using
using System;
using System.Threading.Tasks;
#endregion
/// <summary>
/// Provides some syntactic sugar to run tasks.
/// </summary>
public sealed class TaskHelpers
{
/// <summary>
/// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
/// </summary>
/// <param name="action">The work to execute asynchronously</param>
/// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
/// <exception cref="T:System.ArgumentNullException">
/// The <paramref name="action"/> parameter was null.
/// </exception>
public static Task Run(Action action)
{
return Task.Factory.StartNew(action);
}
/// <summary>
/// Queues the specified work to run on the ThreadPool and returns a proxy for the
/// Task(TResult) returned by <paramref name="function"/>.
/// </summary>
/// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
/// <param name="function">The work to execute asynchronously</param>
/// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
/// <exception cref="T:System.ArgumentNullException">
/// The <paramref name="function"/> parameter was null.
/// </exception>
public static Task<TResult> Run<TResult>(Func<TResult> function)
{
return Task<TResult>.Factory.StartNew(function);
}
}
}

130
src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs

@ -1,130 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageFactoryExtensions.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Extends the ImageFactory class to provide a fluent API.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web
{
#region Using
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using ImageProcessor.Extensions;
using ImageProcessor.Imaging;
using ImageProcessor.Processors;
using ImageProcessor.Web.Config;
#endregion
/// <summary>
/// Extends the ImageFactory class to provide a fluent API.
/// </summary>
public static class ImageFactoryExtensions
{
/// <summary>
/// The object to lock against.
/// </summary>
private static readonly object SyncRoot = new object();
/// <summary>
/// Auto processes image files based on any query string parameters added to the image path.
/// </summary>
/// <param name="factory">
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class
/// that this method extends.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public static ImageFactory AutoProcess(this ImageFactory factory)
{
if (factory.ShouldProcess)
{
// It's faster to lock and run through our activated list than to create new instances.
lock (SyncRoot)
{
// Get a list of all graphics processors that have parsed and matched the query string.
List<IGraphicsProcessor> graphicsProcessors =
ImageProcessorConfig.Instance.GraphicsProcessors
.Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue)
.OrderBy(y => y.SortOrder)
.ToList();
// Loop through and process the image.
foreach (IGraphicsProcessor graphicsProcessor in graphicsProcessors)
{
ApplyProcessor(graphicsProcessor.ProcessImage, factory);
}
}
}
return factory;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="processor">
/// The processor.
/// </param>
/// <param name="factory">
/// The factory.
/// </param>
private static void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory)
{
ImageInfo imageInfo = factory.Image.GetImageInfo(factory.ImageFormat);
if (imageInfo.IsAnimated)
{
OctreeQuantizer quantizer = new OctreeQuantizer(255, 8);
// We don't dispose of the memory stream as that is disposed when a new image is created and doing so
// beforehand will cause an exception.
MemoryStream stream = new MemoryStream();
using (GifEncoder encoder = new GifEncoder(stream, null, null, imageInfo.LoopCount))
{
foreach (GifFrame frame in imageInfo.GifFrames)
{
factory.Update(frame.Image);
frame.Image = quantizer.Quantize(processor.Invoke(factory));
encoder.AddFrame(frame);
}
}
stream.Position = 0;
factory.Update(Image.FromStream(stream));
}
else
{
factory.Update(processor.Invoke(factory));
}
// Set the property item information from any Exif metadata.
// We do this here so that they can be changed between processor methods.
if (factory.PreserveExifData)
{
foreach (KeyValuePair<int, PropertyItem> propertItem in factory.ExifPropertyItems)
{
try
{
factory.Image.SetPropertyItem(propertItem.Value);
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
// Do nothing. The image format does not handle EXIF data.
// TODO: empty catch is fierce code smell.
}
}
}
}
}
}

18
src/ImageProcessor.Web/NET45/Settings.StyleCop

@ -1,18 +0,0 @@
<StyleCopSettings Version="105">
<GlobalSettings>
<CollectionProperty Name="RecognizedWords">
<Value>exif</Value>
<Value>Mutexes</Value>
<Value>querystring</Value>
</CollectionProperty>
</GlobalSettings>
<Analyzers>
<Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
<AnalyzerSettings>
<StringProperty Name="CompanyName">James South</StringProperty>
<StringProperty Name="Copyright">Copyright (c) James South.
Licensed under the Apache License, Version 2.0.</StringProperty>
</AnalyzerSettings>
</Analyzer>
</Analyzers>
</StyleCopSettings>

90
src/ImageProcessor.Web/Processors/Alpha.cs

@ -0,0 +1,90 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Alpha.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to change the alpha component of the image to effect its transparency.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System;
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods to change the alpha component of the image to effect its transparency.
/// </summary>
public class Alpha : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"alpha=[^&|,]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Alpha"/> class.
/// </summary>
public Alpha()
{
this.Processor = new ImageProcessor.Processors.Alpha();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int percentage = Math.Abs(CommonParameterParserUtility.ParseIn100Range(match.Value));
this.Processor.DynamicParameter = percentage;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

94
src/ImageProcessor.Web/Processors/AutoRotate.cs

@ -0,0 +1,94 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AutoRotate.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Performs auto-rotation to ensure that EXIF defined rotation is reflected in
// the final image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
/// <summary>
/// Performs auto-rotation to ensure that EXIF defined rotation is reflected in
/// the final image.
/// </summary>
public class AutoRotate : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"autorotate=true", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="AutoRotate"/> class.
/// </summary>
public AutoRotate()
{
this.Processor = new ImageProcessor.Processors.AutoRotate();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

88
src/ImageProcessor.Web/Processors/BackgroundColor.cs

@ -0,0 +1,88 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BackgroundColor.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Changes the background color of an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Changes the background color of an image.
/// </summary>
public class BackgroundColor : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"bgcolor(=|-)[^&]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundColor"/> class.
/// </summary>
public BackgroundColor()
{
this.Processor = new ImageProcessor.Processors.BackgroundColor();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
this.Processor.DynamicParameter = CommonParameterParserUtility.ParseColor(match.Value);
}
index += 1;
}
}
return this.SortOrder;
}
}
}

89
src/ImageProcessor.Web/Processors/Brightness.cs

@ -0,0 +1,89 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Brightness.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to change the brightness component of the image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods to change the brightness component of the image.
/// </summary>
public class Brightness : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"brightness=[^&|,]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Brightness"/> class.
/// </summary>
public Brightness()
{
this.Processor = new ImageProcessor.Processors.Brightness();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int percentage = CommonParameterParserUtility.ParseIn100Range(match.Value);
this.Processor.DynamicParameter = percentage;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

89
src/ImageProcessor.Web/Processors/Contrast.cs

@ -0,0 +1,89 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Contrast.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to change the contrast component of the image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods to change the contrast component of the image.
/// </summary>
public class Contrast : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"contrast=[^&|,]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Contrast"/> class.
/// </summary>
public Contrast()
{
this.Processor = new ImageProcessor.Processors.Contrast();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int percentage = CommonParameterParserUtility.ParseIn100Range(match.Value);
this.Processor.DynamicParameter = percentage;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

167
src/ImageProcessor.Web/Processors/Crop.cs

@ -0,0 +1,167 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Crop.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Crops an image to the given directions.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging;
using ImageProcessor.Processors;
using ImageProcessor.Web.Extensions;
/// <summary>
/// Crops an image to the given directions.
/// </summary>
public class Crop : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// <see href="http://stackoverflow.com/a/6400969/427899"/>
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"(crop=|cropmode=)[^&]+", RegexOptions.Compiled);
/// <summary>
/// The coordinate regex.
/// </summary>
private static readonly Regex CoordinateRegex = new Regex(@"crop=\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?", RegexOptions.Compiled);
/// <summary>
/// The mode regex.
/// </summary>
private static readonly Regex ModeRegex = new Regex(@"cropmode=(pixels|percent)", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Crop"/> class.
/// </summary>
public Crop()
{
this.Processor = new ImageProcessor.Processors.Crop();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
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();
float[] coordinates = this.ParseCoordinates(toParse);
CropMode cropMode = this.ParseMode(toParse);
CropLayer cropLayer = new CropLayer(coordinates[0], coordinates[1], coordinates[2], coordinates[3], cropMode);
this.Processor.DynamicParameter = cropLayer;
}
return this.SortOrder;
}
/// <summary>
/// Returns the correct <see cref="CropMode"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="CropMode"/>.
/// </returns>
private CropMode ParseMode(string input)
{
foreach (Match match in ModeRegex.Matches(input))
{
// Split on =
string mode = match.Value.Split('=')[1];
switch (mode)
{
case "percent":
return CropMode.Percentage;
case "pixels":
return CropMode.Pixels;
}
}
return CropMode.Pixels;
}
/// <summary>
/// Returns the correct <see cref="CropMode"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="CropMode"/>.
/// </returns>
private float[] ParseCoordinates(string input)
{
float[] floats = { };
foreach (Match match in CoordinateRegex.Matches(input))
{
floats = match.Value.ToPositiveFloatArray();
}
return floats;
}
}
}

161
src/ImageProcessor.Web/Processors/Filter.cs

@ -0,0 +1,161 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Filter.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods with which to add filters to an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging.Filters;
using ImageProcessor.Processors;
/// <summary>
/// Encapsulates methods with which to add filters to an image.
/// </summary>
public class Filter : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = BuildRegex();
/// <summary>
/// Initializes a new instance of the <see cref="Filter"/> class.
/// </summary>
public Filter()
{
this.Processor = new ImageProcessor.Processors.Filter();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets the processor.
/// </summary>
/// <value>
/// The processor.
/// </value>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
this.Processor.DynamicParameter = this.ParseFilter(match.Value.Split('=')[1]);
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Builds a regular expression from the <see cref="MatrixFilters"/> type, this allows extensibility.
/// </summary>
/// <returns>
/// The <see cref="Regex"/> to match matrix filters.
/// </returns>
private static Regex BuildRegex()
{
const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static;
Type type = typeof(MatrixFilters);
IEnumerable<PropertyInfo> filters = type.GetProperties(Flags)
.Where(p => p.PropertyType.IsAssignableFrom(typeof(IMatrixFilter)))
.ToList();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("filter=(");
int counter = 0;
foreach (PropertyInfo filter in filters)
{
if (counter == 0)
{
stringBuilder.Append(filter.Name.ToLowerInvariant());
}
else
{
stringBuilder.AppendFormat("|{0}", filter.Name.ToLowerInvariant());
}
counter++;
}
stringBuilder.Append(")");
return new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase);
}
/// <summary>
/// Parses the input string to return the correct <see cref="IMatrixFilter"/>.
/// </summary>
/// <param name="identifier">
/// The identifier.
/// </param>
/// <returns>
/// The <see cref="IMatrixFilter"/>.
/// </returns>
private IMatrixFilter ParseFilter(string identifier)
{
const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static;
Type type = typeof(MatrixFilters);
PropertyInfo filter =
type.GetProperties(Flags)
.Where(p => p.PropertyType.IsAssignableFrom(typeof(IMatrixFilter)))
.First(p => p.Name.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
return filter.GetValue(null, null) as IMatrixFilter;
}
}
}

103
src/ImageProcessor.Web/Processors/Flip.cs

@ -0,0 +1,103 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Flip.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Flips an image horizontally or vertically.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Drawing;
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
/// <summary>
/// Flips an image horizontally or vertically.
/// </summary>
public class Flip : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"flip=(horizontal|vertical|both)", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Flip"/> class.
/// </summary>
public Flip()
{
this.Processor = new ImageProcessor.Processors.Flip();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
string direction = match.Value.Split('=')[1];
switch (direction)
{
case "horizontal":
this.Processor.DynamicParameter = RotateFlipType.RotateNoneFlipX;
break;
case "vertical":
this.Processor.DynamicParameter = RotateFlipType.RotateNoneFlipY;
break;
default:
this.Processor.DynamicParameter = RotateFlipType.RotateNoneFlipXY;
break;
}
}
index += 1;
}
}
return this.SortOrder;
}
}
}

166
src/ImageProcessor.Web/Processors/Format.cs

@ -0,0 +1,166 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Format.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Sets the output of the image to a specific format.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Configuration;
using ImageProcessor.Imaging.Formats;
using ImageProcessor.Processors;
/// <summary>
/// Sets the output of the image to a specific format.
/// </summary>
public class Format : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = BuildRegex();
/// <summary>
/// Initializes a new instance of the <see cref="Format"/> class.
/// </summary>
public Format()
{
this.Processor = new ImageProcessor.Processors.Format();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
ISupportedImageFormat format = this.ParseFormat(match.Value.Split('=')[1]);
if (format != null)
{
this.Processor.DynamicParameter = format;
}
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Builds a regular expression from the <see cref="T:ImageProcessor.Imaging.Formats.ISupportedImageFormat"/> type, this allows extensibility.
/// </summary>
/// <returns>
/// The <see cref="Regex"/> to match matrix filters.
/// </returns>
private static Regex BuildRegex()
{
StringBuilder stringBuilder = new StringBuilder();
// png8 is a special case for determining indexed pngs.
stringBuilder.Append("format=(png8");
foreach (ISupportedImageFormat imageFormat in ImageProcessorBootstrapper.Instance.SupportedImageFormats)
{
foreach (string fileExtension in imageFormat.FileExtensions)
{
stringBuilder.AppendFormat("|{0}", fileExtension.ToLowerInvariant());
}
}
stringBuilder.Append(")");
return new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase);
}
/// <summary>
/// Parses the input string to return the correct <see cref="ISupportedImageFormat"/>.
/// </summary>
/// <param name="identifier">
/// The identifier.
/// </param>
/// <returns>
/// The <see cref="ISupportedImageFormat"/>.
/// </returns>
private ISupportedImageFormat ParseFormat(string identifier)
{
identifier = identifier.ToLowerInvariant();
string finalIdentifier = identifier.Equals("png8") ? "png" : identifier;
ISupportedImageFormat newFormat = null;
List<ISupportedImageFormat> formats = ImageProcessorBootstrapper.Instance.SupportedImageFormats.ToList();
ISupportedImageFormat format = formats.FirstOrDefault(f => f.FileExtensions.Any(e => e.Equals(finalIdentifier, StringComparison.InvariantCultureIgnoreCase)));
if (format != null)
{
// Return a new instance as we want to use instance properties.
newFormat = Activator.CreateInstance(format.GetType()) as ISupportedImageFormat;
if (newFormat != null)
{
// I wish this wasn't hard-coded but there's no way I can
// find to preserve the palette.
if (identifier.Equals("png8"))
{
newFormat.IsIndexed = true;
}
else if (identifier.Equals("png"))
{
newFormat.IsIndexed = false;
}
}
}
return newFormat;
}
}
}

101
src/ImageProcessor.Web/Processors/GaussianBlur.cs

@ -0,0 +1,101 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GaussianBlur.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Applies a Gaussian blur to the image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Globalization;
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
public class GaussianBlur : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"blur=[^&]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="GaussianBlur"/> class.
/// </summary>
public GaussianBlur()
{
this.Processor = new ImageProcessor.Processors.GaussianBlur();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
// Normalise and set the variables.
int maxSize;
double maxSigma;
int maxThreshold;
int.TryParse(this.Processor.Settings["MaxSize"], NumberStyles.Any, CultureInfo.InvariantCulture, out maxSize);
double.TryParse(this.Processor.Settings["MaxSigma"], NumberStyles.Any, CultureInfo.InvariantCulture, out maxSigma);
int.TryParse(this.Processor.Settings["MaxThreshold"], NumberStyles.Any, CultureInfo.InvariantCulture, out maxThreshold);
this.Processor.DynamicParameter = CommonParameterParserUtility.ParseGaussianLayer(queryString, maxSize, maxSigma, maxThreshold);
}
index += 1;
}
}
return this.SortOrder;
}
}
}

101
src/ImageProcessor.Web/Processors/GaussianSharpen.cs

@ -0,0 +1,101 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GaussianSharpen.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Applies a Gaussian sharpen to the image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Globalization;
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Applies a Gaussian sharpen to the image.
/// </summary>
public class GaussianSharpen : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"sharpen=[^&]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="GaussianSharpen"/> class.
/// </summary>
public GaussianSharpen()
{
this.Processor = new ImageProcessor.Processors.GaussianSharpen();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
// Normalise and set the variables.
int maxSize;
double maxSigma;
int maxThreshold;
int.TryParse(this.Processor.Settings["MaxSize"], NumberStyles.Any, CultureInfo.InvariantCulture, out maxSize);
double.TryParse(this.Processor.Settings["MaxSigma"], NumberStyles.Any, CultureInfo.InvariantCulture, out maxSigma);
int.TryParse(this.Processor.Settings["MaxThreshold"], NumberStyles.Any, CultureInfo.InvariantCulture, out maxThreshold);
this.Processor.DynamicParameter = CommonParameterParserUtility.ParseGaussianLayer(queryString, maxSize, maxSigma, maxThreshold);
}
index += 1;
}
}
return this.SortOrder;
}
}
}

51
src/ImageProcessor.Web/Processors/IWebGraphicsProcessor.cs

@ -0,0 +1,51 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="IWebGraphicsProcessor.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Defines properties and methods for ImageProcessor.Web Plugins.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
/// <summary>
/// Defines properties and methods for ImageProcessor.Web Plugins.
/// </summary>
public interface IWebGraphicsProcessor
{
#region Properties
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
Regex RegexPattern { get; }
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
int SortOrder { get; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
IGraphicsProcessor Processor { get; }
#endregion
#region Methods
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
int MatchRegexIndex(string queryString);
#endregion
}
}

92
src/ImageProcessor.Web/Processors/Meta.cs

@ -0,0 +1,92 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Meta.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to control preservation of meta information.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
/// <summary>
/// Encapsulates methods to control preservation of meta information.
/// </summary>
public class Meta : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"meta=(true|false)", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Meta"/> class.
/// </summary>
public Meta()
{
this.Processor = new ImageProcessor.Processors.Meta();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
bool preserve = bool.Parse(match.Value.Split('=')[1]);
this.Processor.DynamicParameter = preserve;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

90
src/ImageProcessor.Web/Processors/Quality.cs

@ -0,0 +1,90 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Quality.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to change the quality component of the image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System;
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods to change the quality component of the image.
/// </summary>
public class Quality : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"quality=[^&|,]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Quality"/> class.
/// </summary>
public Quality()
{
this.Processor = new ImageProcessor.Processors.Quality();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int percentage = Math.Abs(CommonParameterParserUtility.ParseIn100Range(match.Value));
this.Processor.DynamicParameter = percentage;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

325
src/ImageProcessor.Web/Processors/Resize.cs

@ -0,0 +1,325 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Resize.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Resizes an image to the given dimensions.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging;
using ImageProcessor.Processors;
using ImageProcessor.Web.Extensions;
/// <summary>
/// Resizes an image to the given dimensions.
/// </summary>
public class Resize : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"(width|height)=|(width|height)ratio=|mode=|anchor=|center=|upscale=", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the size attribute.
/// </summary>
private static readonly Regex SizeRegex = new Regex(@"(width|height)=\d+", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the ratio attribute.
/// </summary>
private static readonly Regex RatioRegex = new Regex(@"(width|height)ratio=\d+(.\d+)?", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the mode attribute.
/// </summary>
private static readonly Regex ModeRegex = new Regex(@"mode=(pad|stretch|crop|max)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the anchor attribute.
/// </summary>
private static readonly Regex AnchorRegex = new Regex(@"anchor=(top|bottom|left|right|center)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the center attribute.
/// </summary>
private static readonly Regex CenterRegex = new Regex(@"center=\d+(.\d+)?[,-]\d+(.\d+)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the upscale attribute.
/// </summary>
private static readonly Regex UpscaleRegex = new Regex(@"upscale=false", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Resize"/> class.
/// </summary>
public Resize()
{
this.Processor = new ImageProcessor.Processors.Resize();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
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(queryString);
}
index += 1;
}
}
// Match syntax
string toParse = stringBuilder.ToString();
Size size = this.ParseSize(toParse);
ResizeLayer resizeLayer = new ResizeLayer(size)
{
ResizeMode = this.ParseMode(toParse),
AnchorPosition = this.ParsePosition(toParse),
Upscale = !UpscaleRegex.IsMatch(toParse),
CenterCoordinates = this.ParseCoordinates(toParse),
};
this.Processor.DynamicParameter = resizeLayer;
// Correctly parse any restrictions.
string restrictions;
this.Processor.Settings.TryGetValue("RestrictTo", out restrictions);
((ImageProcessor.Processors.Resize)this.Processor).RestrictedSizes = this.ParseRestrictions(restrictions);
return this.SortOrder;
}
/// <summary>
/// Returns the correct <see cref="Size"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The <see cref="Size"/>.
/// </returns>
private Size ParseSize(string input)
{
const string Width = "width=";
const string Height = "height=";
const string WidthRatio = "widthratio=";
const string HeightRatio = "heightratio=";
Size size = new Size();
// First merge the matches so we can parse .
StringBuilder stringBuilder = new StringBuilder();
foreach (Match match in SizeRegex.Matches(input))
{
stringBuilder.Append(match.Value);
}
// First cater for single dimensions.
string value = stringBuilder.ToString();
if (input.Contains(Width) && !input.Contains(Height))
{
size = new Size(value.ToPositiveIntegerArray()[0], 0);
}
if (input.Contains(Height) && !input.Contains(Width))
{
size = new Size(0, value.ToPositiveIntegerArray()[0]);
}
// Both dimensions supplied.
if (input.Contains(Height) && input.Contains(Width))
{
int[] dimensions = value.ToPositiveIntegerArray();
// Check the order in which they have been supplied.
size = input.IndexOf(Width, StringComparison.Ordinal) < input.IndexOf(Height, StringComparison.Ordinal)
? new Size(dimensions[0], dimensions[1])
: new Size(dimensions[1], dimensions[0]);
}
// Calculate any ratio driven sizes.
if (size.Width == 0 || size.Height == 0)
{
stringBuilder.Clear();
foreach (Match match in RatioRegex.Matches(input))
{
stringBuilder.Append(match.Value);
}
value = stringBuilder.ToString();
// Replace 0 width
if (size.Width == 0 && size.Height > 0 && input.Contains(WidthRatio) && !input.Contains(HeightRatio))
{
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)Math.Ceiling(value.ToPositiveFloatArray()[0] * size.Width);
}
}
return size;
}
/// <summary>
/// Returns the correct <see cref="ResizeMode"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="ResizeMode"/>.
/// </returns>
private ResizeMode ParseMode(string input)
{
foreach (Match match in ModeRegex.Matches(input))
{
// Split on =
string mode = match.Value.Split('=')[1];
switch (mode)
{
case "stretch":
return ResizeMode.Stretch;
case "crop":
return ResizeMode.Crop;
case "max":
return ResizeMode.Max;
default:
return ResizeMode.Pad;
}
}
return ResizeMode.Pad;
}
/// <summary>
/// Returns the correct <see cref="AnchorPosition"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="AnchorPosition"/>.
/// </returns>
private AnchorPosition ParsePosition(string input)
{
foreach (Match match in AnchorRegex.Matches(input))
{
// Split on =
string anchor = match.Value.Split('=')[1];
switch (anchor)
{
case "top":
return AnchorPosition.Top;
case "bottom":
return AnchorPosition.Bottom;
case "left":
return AnchorPosition.Left;
case "right":
return AnchorPosition.Right;
default:
return AnchorPosition.Center;
}
}
return AnchorPosition.Center;
}
/// <summary>
/// Parses the coordinates.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>The <see cref="float"/> array containing the coordinates</returns>
private float[] ParseCoordinates(string input)
{
float[] floats = { };
foreach (Match match in CenterRegex.Matches(input))
{
floats = match.Value.ToPositiveFloatArray();
}
return floats;
}
/// <summary>
/// Returns a <see cref="List{T}"/> of sizes to restrict resizing to.
/// </summary>
/// <param name="input">
/// The input.
/// </param>
/// <returns>
/// The <see cref="List{Size}"/> to restrict resizing to.
/// </returns>
private List<Size> ParseRestrictions(string input)
{
List<Size> sizes = new List<Size>();
if (!string.IsNullOrWhiteSpace(input))
{
sizes.AddRange(input.Split(',').Select(this.ParseSize));
}
return sizes;
}
}
}

96
src/ImageProcessor.Web/Processors/Rotate.cs

@ -0,0 +1,96 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Rotate.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to rotate an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods to rotate an image.
/// </summary>
public class Rotate : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"(rotate|angle)(=|-)[^&|,]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Rotate"/> class.
/// </summary>
public Rotate()
{
this.Processor = new ImageProcessor.Processors.Rotate();
}
#region IGraphicsProcessor Members
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
this.Processor.DynamicParameter = CommonParameterParserUtility.ParseAngle(match.Value);
}
index += 1;
}
}
return this.SortOrder;
}
#endregion
}
}

173
src/ImageProcessor.Web/Processors/RoundedCorners.cs

@ -0,0 +1,173 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="RoundedCorners.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to add rounded corners to an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Globalization;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging;
using ImageProcessor.Processors;
/// <summary>
/// Encapsulates methods to add rounded corners to an image.
/// </summary>
public class RoundedCorners : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"roundedcorners=[^&]+", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the angle attribute.
/// </summary>
private static readonly Regex RadiusRegex = new Regex(@"(roundedcorners|radius)(=|-)(\d+)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the top left attribute.
/// </summary>
private static readonly Regex TopLeftRegex = new Regex(@"tl(=|-)(true|false)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the top right attribute.
/// </summary>
private static readonly Regex TopRightRegex = new Regex(@"tr(=|-)(true|false)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the bottom left attribute.
/// </summary>
private static readonly Regex BottomLeftRegex = new Regex(@"bl(=|-)(true|false)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the bottom right attribute.
/// </summary>
private static readonly Regex BottomRightRegex = new Regex(@"br(=|-)(true|false)", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="RoundedCorners"/> class.
/// </summary>
public RoundedCorners()
{
this.Processor = new ImageProcessor.Processors.RoundedCorners();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
RoundedCornerLayer roundedCornerLayer = new RoundedCornerLayer(
this.ParseRadius(queryString),
this.ParseCorner(TopLeftRegex, queryString),
this.ParseCorner(TopRightRegex, queryString),
this.ParseCorner(BottomLeftRegex, queryString),
this.ParseCorner(BottomRightRegex, queryString));
this.Processor.DynamicParameter = roundedCornerLayer;
}
index += 1;
}
}
return this.SortOrder;
}
#region Private Methods
/// <summary>
/// Returns the correct <see cref="T:System.Int32"/> containing the radius for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Int32"/> containing the radius for the given string.
/// </returns>
private int ParseRadius(string input)
{
foreach (Match match in RadiusRegex.Matches(input))
{
// Split on radius-
int radius;
int.TryParse(match.Value.Split(new[] { '=', '-' })[1], NumberStyles.Any, CultureInfo.InvariantCulture, out radius);
return radius;
}
// No corners - matches the RoundedCorner default.
return 0;
}
/// <summary>
/// Returns a <see cref="T:System.Boolean"/> either true or false.
/// </summary>
/// <param name="corner">
/// The corner.
/// </param>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Boolean"/> true or false.
/// </returns>
private bool ParseCorner(Regex corner, string input)
{
foreach (Match match in corner.Matches(input))
{
// Split on corner-
bool cornerRound;
bool.TryParse(match.Value.Split(new[] { '=', '-' })[1], out cornerRound);
return cornerRound;
}
// No corners - matches the RoundedCorner default.
return true;
}
#endregion
}
}

92
src/ImageProcessor.Web/Processors/Saturation.cs

@ -0,0 +1,92 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Saturation.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to change the saturation component of the image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods to change the saturation component of the image.
/// </summary>
/// <remarks>
/// <see href="http://www.bobpowell.net/imagesaturation.htm"/>
/// </remarks>
public class Saturation : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"saturation=[^&|,]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Saturation"/> class.
/// </summary>
public Saturation()
{
this.Processor = new ImageProcessor.Processors.Saturation();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int percentage = CommonParameterParserUtility.ParseIn100Range(match.Value);
this.Processor.DynamicParameter = percentage;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

85
src/ImageProcessor.Web/Processors/Tint.cs

@ -0,0 +1,85 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Tint.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Tints an image with the given color.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Tints an image with the given color.
/// </summary>
public class Tint : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"tint=[^&]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Tint"/> class.
/// </summary>
public Tint()
{
this.Processor = new ImageProcessor.Processors.Tint();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get { return QueryRegex; }
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">The query string to search.</param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
this.Processor.DynamicParameter = CommonParameterParserUtility.ParseColor(match.Value);
}
index += 1;
}
}
return this.SortOrder;
}
}
}

98
src/ImageProcessor.Web/Processors/Vignette.cs

@ -0,0 +1,98 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Vignette.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods with which to add a vignette image effect to an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Drawing;
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods with which to add a vignette image effect to an image.
/// </summary>
public class Vignette : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"vignette(=true)?[^&]+", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Vignette"/> class.
/// </summary>
public Vignette()
{
this.Processor = new ImageProcessor.Processors.Vignette();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
Color color = CommonParameterParserUtility.ParseColor(match.Value);
if (color.Equals(Color.Transparent))
{
color = Color.Black;
}
this.Processor.DynamicParameter = color;
}
index += 1;
}
}
return this.SortOrder;
}
}
}

341
src/ImageProcessor.Web/Processors/Watermark.cs

@ -0,0 +1,341 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Watermark.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to add a watermark text overlay to an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Processors
{
using System.Drawing;
using System.Drawing.Text;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging;
using ImageProcessor.Processors;
using ImageProcessor.Web.Extensions;
using ImageProcessor.Web.Helpers;
/// <summary>
/// Encapsulates methods to add a watermark text overlay to an image.
/// </summary>
public class Watermark : IWebGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"watermark=[^&]+", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the text attribute.
/// </summary>
private static readonly Regex TextRegex = new Regex(@"(watermark=[^text-]|text-)[^/:?#\[\]@!$&'()*%\|,;=&]+", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the position attribute.
/// </summary>
private static readonly Regex PositionRegex = new Regex(@"(text)?position(=|-)\d+[-,]\d+", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the font size attribute.
/// </summary>
private static readonly Regex FontSizeRegex = new Regex(@"((font)?)size(=|-)\d{1,3}", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the font style attribute.
/// </summary>
private static readonly Regex FontStyleRegex = new Regex(@"((font)?)style(=|-)(bold|italic|regular|strikeout|underline)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the font family attribute.
/// </summary>
private static readonly Regex FontFamilyRegex = new Regex(@"font(family)?(=|-)[^/:?#\[\]@!$&'()*%\|,;=0-9]+", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the opacity attribute.
/// </summary>
private static readonly Regex OpacityRegex = new Regex(@"((font)?)opacity(=|-)(?:100|[1-9]?[0-9])", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the shadow attribute.
/// </summary>
private static readonly Regex ShadowRegex = new Regex(@"((text|font|drop)?)shadow(=|-)true", RegexOptions.Compiled);
/// <summary>
/// Initializes a new instance of the <see cref="Watermark"/> class.
/// </summary>
public Watermark()
{
this.Processor = new ImageProcessor.Processors.Watermark();
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder { get; private set; }
/// <summary>
/// Gets the associated graphics processor.
/// </summary>
public IGraphicsProcessor Processor { get; private set; }
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
public int MatchRegexIndex(string queryString)
{
int index = 0;
// Set the sort order to max to allow filtering.
this.SortOrder = int.MaxValue;
foreach (Match match in this.RegexPattern.Matches(queryString))
{
if (match.Success)
{
if (index == 0)
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
TextLayer textLayer = new TextLayer
{
Text = this.ParseText(queryString),
Position = this.ParsePosition(queryString),
FontColor = this.ParseColor(queryString),
FontSize = this.ParseFontSize(queryString),
FontFamily = this.ParseFontFamily(queryString),
Style = this.ParseFontStyle(queryString),
DropShadow = this.ParseDropShadow(queryString)
};
textLayer.Opacity = this.ParseOpacity(queryString, textLayer.FontColor);
this.Processor.DynamicParameter = textLayer;
}
index += 1;
}
}
return this.SortOrder;
}
#region Private Methods
/// <summary>
/// Returns the correct <see cref="T:System.String"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.String"/> for the given string.
/// </returns>
private string ParseText(string input)
{
foreach (Match match in TextRegex.Matches(input))
{
// split on text-
return match.Value.Split(new[] { '=', '-' })[1].Replace("+", " ");
}
return string.Empty;
}
/// <summary>
/// Returns the correct <see cref="T:System.Drawing.Point"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Drawing.Point"/>
/// </returns>
private Point ParsePosition(string input)
{
foreach (Match match in PositionRegex.Matches(input))
{
int[] position = match.Value.ToPositiveIntegerArray();
if (position != null)
{
int x = position[0];
int y = position[1];
return new Point(x, y);
}
}
return Point.Empty;
}
/// <summary>
/// Returns the correct <see cref="T:System.Drawing.Color"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Drawing.Color"/>
/// </returns>
private Color ParseColor(string input)
{
Color textColor = CommonParameterParserUtility.ParseColor(input);
if (!textColor.Equals(Color.Transparent))
{
return textColor;
}
return Color.Black;
}
/// <summary>
/// Returns the correct <see cref="T:System.Int32"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Int32"/>
/// </returns>
private int ParseFontSize(string input)
{
foreach (Match match in FontSizeRegex.Matches(input))
{
// split on size-value
return int.Parse(match.Value.Split(new[] { '=', '-' })[1], CultureInfo.InvariantCulture);
}
// Matches the default number in TextLayer.
return 48;
}
/// <summary>
/// Returns the correct <see cref="T:System.Drawing.FontStyle"/> for the given string.
/// </summary>
/// <param name="input">
/// The string containing the respective font style.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Drawing.FontStyle"/>
/// </returns>
private FontStyle ParseFontStyle(string input)
{
FontStyle fontStyle = FontStyle.Bold;
foreach (Match match in FontStyleRegex.Matches(input))
{
// split on style-
switch (match.Value.Split(new[]
{
'=', '-'
})[1])
{
case "italic":
fontStyle = FontStyle.Italic;
break;
case "regular":
fontStyle = FontStyle.Regular;
break;
case "strikeout":
fontStyle = FontStyle.Strikeout;
break;
case "underline":
fontStyle = FontStyle.Underline;
break;
}
}
return fontStyle;
}
/// <summary>
/// Returns the correct <see cref="T:System.String"/> containing the font family for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="T:System.String"/> containing the font family for the given string.
/// </returns>
private FontFamily ParseFontFamily(string input)
{
foreach (Match match in FontFamilyRegex.Matches(input))
{
// split on font-
string font = match.Value.Split(new[] { '=', '-' })[1].Replace("+", " ");
return new FontFamily(font);
}
return new FontFamily(GenericFontFamilies.SansSerif);
}
/// <summary>
/// Returns the correct <see cref="T:System.Int32"/> containing the opacity for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <param name="color">
/// The <see cref="T:System.Drawing.Color"/> of the current <see cref="TextLayer"/>.
/// </param>
/// <returns>
/// The correct <see cref="T:System.Int32"/> containing the opacity for the given string.
/// </returns>
private int ParseOpacity(string input, Color color)
{
if (color.A < 255)
{
return (color.A / 255) * 100;
}
foreach (Match match in OpacityRegex.Matches(input))
{
// Split on opacity-
return int.Parse(match.Value.Split(new[] { '=', '-' })[1], CultureInfo.InvariantCulture);
}
// Full opacity - matches the TextLayer default.
return 100;
}
/// <summary>
/// Returns a value indicating whether the watermark is to have a shadow.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The true if the watermark is to have a shadow; otherwise false.
/// </returns>
private bool ParseDropShadow(string input)
{
return ShadowRegex.Matches(input).Cast<Match>().Any();
}
#endregion
}
}

31
src/ImageProcessor.Web/NET45/Properties/AssemblyInfo.cs → src/ImageProcessor.Web/Properties/AssemblyInfo.cs

@ -1,14 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AssemblyInfo.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// AssemblyInfo.cs
// </summary>
// --------------------------------------------------------------------------------------------------------------------
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// 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.
using System.Web;
using ImageProcessor.Web.HttpModules;
[assembly: AssemblyTitle("ImageProcessor.Web")]
[assembly: AssemblyDescription("A library for on-the-fly processing of image files with ASP.NET written in C#")]
[assembly: AssemblyConfiguration("James South")]
@ -18,8 +23,8 @@ using ImageProcessor.Web.HttpModules;
[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
// 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)]
@ -29,11 +34,11 @@ using ImageProcessor.Web.HttpModules;
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("3.3.0.0")]
[assembly: AssemblyFileVersion("3.3.0.0")]
[assembly: AssemblyVersion("4.0.0.0")]
[assembly: AssemblyFileVersion("4.0.0.0")]

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save