Browse Source

Initial commit

THIS WON'T BUILD JUST NOW.


Former-commit-id: 0392cced86a9ee8dd5f8139fad5c465a83171562
af/merge-core
James South 12 years ago
parent
commit
166df34924
  1. 8
      src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
  2. 4
      src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs
  3. 2
      src/ImageProcessor.Web/NET45/Config/ImageProcessingSection.cs
  4. 12
      src/ImageProcessor.Web/NET45/Config/ImageProcessorConfiguration.cs
  5. 2
      src/ImageProcessor.Web/NET45/Config/ImageSecuritySection.cs
  6. 90
      src/ImageProcessor.Web/NET45/Helpers/ParameterParserUtilities.cs
  7. 12
      src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs
  8. 13
      src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
  9. 71
      src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs
  10. 6
      src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj
  11. 88
      src/ImageProcessor.Web/NET45/Processors/BackgroundColor.cs
  12. 51
      src/ImageProcessor.Web/NET45/Processors/IWebGraphicsProcessor.cs
  13. 96
      src/ImageProcessor.Web/NET45/Processors/Rotate.cs
  14. 24
      src/ImageProcessor.Web/NET45/Processors/RoundedCorners.cs
  15. 81
      src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs
  16. 39
      src/ImageProcessor/Core/Common/Exceptions/ImageFormatException.cs
  17. 39
      src/ImageProcessor/Core/Common/Exceptions/ImageProcessingException.cs
  18. 2
      src/ImageProcessor/Core/Common/Extensions/DoubleExtensions.cs
  19. 3
      src/ImageProcessor/Core/Common/Extensions/ImageExtensions.cs
  20. 4
      src/ImageProcessor/Core/Common/Extensions/ImageFormatExtensions.cs
  21. 3
      src/ImageProcessor/Core/Common/Extensions/ImageInfo.cs
  22. 2
      src/ImageProcessor/Core/Common/Extensions/IntegerExtensions.cs
  23. 4
      src/ImageProcessor/Core/Common/Extensions/StringExtensions.cs
  24. 332
      src/ImageProcessor/ImageFactory.cs
  25. 24
      src/ImageProcessor/ImageProcessor.csproj
  26. 3
      src/ImageProcessor/Imaging/Convolution.cs
  27. 2
      src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs
  28. 65
      src/ImageProcessor/Imaging/Formats/BitmapFormat.cs
  29. 123
      src/ImageProcessor/Imaging/Formats/FormatBase.cs
  30. 88
      src/ImageProcessor/Imaging/Formats/FormatUtilities.cs
  31. 149
      src/ImageProcessor/Imaging/Formats/GifFormat.cs
  32. 113
      src/ImageProcessor/Imaging/Formats/ISupportedImageFormat.cs
  33. 155
      src/ImageProcessor/Imaging/Formats/JpegFormat.cs
  34. 103
      src/ImageProcessor/Imaging/Formats/PngFormat.cs
  35. 96
      src/ImageProcessor/Imaging/Formats/TiffFormat.cs
  36. 74
      src/ImageProcessor/Processors/BackgroundColor.cs
  37. 2
      src/ImageProcessor/Processors/Crop.cs
  38. 3
      src/ImageProcessor/Processors/Format.cs
  39. 27
      src/ImageProcessor/Processors/IGraphicsProcessor.cs
  40. 2
      src/ImageProcessor/Processors/Quality.cs
  41. 3
      src/ImageProcessor/Processors/Resize.cs
  42. 150
      src/ImageProcessor/Processors/Rotate.cs
  43. 3
      src/ImageProcessor/Processors/Tint.cs
  44. 2
      src/ImageProcessor/Processors/Watermark.cs
  45. 2
      src/ImageProcessor/Settings.StyleCop
  46. 4
      src/ImageProcessorConsole/Program.cs
  47. 2
      src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id
  48. 2
      src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id

8
src/ImageProcessor.Web/NET45/Caching/DiskCache.cs

@ -21,8 +21,8 @@ namespace ImageProcessor.Web.Caching
using System.Web;
using System.Web.Hosting;
using ImageProcessor.Extensions;
using ImageProcessor.Web.Config;
using ImageProcessor.Core.Common.Extensions;
using ImageProcessor.Web.Configuration;
using ImageProcessor.Web.Helpers;
#endregion
@ -35,7 +35,7 @@ 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.
@ -53,7 +53,7 @@ namespace ImageProcessor.Web.Caching
/// The absolute path to virtual cache path on the server.
/// </summary>
private static readonly string AbsoluteCachePath =
HostingEnvironment.MapPath(ImageProcessorConfig.Instance.VirtualCachePath);
HostingEnvironment.MapPath(ImageProcessorConfiguration.Instance.VirtualCachePath);
/// <summary>
/// The request for the image.

4
src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs

@ -8,14 +8,14 @@
// </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.Core.Common.Extensions;
using ImageProcessor.Web.Helpers;
#endregion

2
src/ImageProcessor.Web/NET45/Config/ImageProcessingSection.cs

@ -9,7 +9,7 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Config
namespace ImageProcessor.Web.Configuration
{
#region Using
using System.Configuration;

12
src/ImageProcessor.Web/NET45/Config/ImageProcessorConfig.cs → src/ImageProcessor.Web/NET45/Config/ImageProcessorConfiguration.cs

@ -8,7 +8,7 @@
// <see cref="http://csharpindepth.com/Articles/General/Singleton.aspx" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Config
namespace ImageProcessor.Web.Configuration
{
#region Using
using System;
@ -24,15 +24,15 @@ namespace ImageProcessor.Web.Config
/// Encapsulates methods to allow the retrieval of ImageProcessor settings.
/// <see cref="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
@ -67,7 +67,7 @@ namespace ImageProcessor.Web.Config
/// <summary>
/// Prevents a default instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class from being created.
/// </summary>
private ImageProcessorConfig()
private ImageProcessorConfiguration()
{
this.LoadGraphicsProcessors();
}
@ -77,7 +77,7 @@ namespace ImageProcessor.Web.Config
/// <summary>
/// Gets the current instance of the <see cref="T:ImageProcessor.Web.Config.ImageProcessorConfig"/> class.
/// </summary>
public static ImageProcessorConfig Instance
public static ImageProcessorConfiguration Instance
{
get
{

2
src/ImageProcessor.Web/NET45/Config/ImageSecuritySection.cs

@ -8,7 +8,7 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.Config
namespace ImageProcessor.Web.Configuration
{
#region Using
using System;

90
src/ImageProcessor.Web/NET45/Helpers/ParameterParserUtilities.cs

@ -0,0 +1,90 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ParameterParserUtilities.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.Drawing;
using System.Globalization;
using System.Text.RegularExpressions;
using ImageProcessor.Core.Common.Extensions;
/// <summary>
/// Encapsulates methods to correctly parse querystring parameters.
/// </summary>
public static class ParameterParserUtilities
{
/// <summary>
/// The regular expression to search strings for colors.
/// </summary>
private static readonly Regex ColorRegex = new Regex(@"(bgcolor|color)(=|-)(\d+,\d+,\d+,\d+|([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled);
/// <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>
/// 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 (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;
}
}
}

12
src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs

@ -19,7 +19,7 @@ namespace ImageProcessor.Web.Helpers
using System.Security;
using System.Text;
using System.Threading.Tasks;
using ImageProcessor.Web.Config;
using ImageProcessor.Web.Configuration;
#endregion
/// <summary>
@ -48,27 +48,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

13
src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs

@ -28,9 +28,10 @@ namespace ImageProcessor.Web.HttpModules
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Extensions;
using ImageProcessor.Web.Caching;
using ImageProcessor.Web.Config;
using ImageProcessor.Web.Configuration;
using ImageProcessor.Web.Helpers;
#endregion
@ -117,12 +118,12 @@ namespace ImageProcessor.Web.HttpModules
{
if (remotePrefix == null)
{
remotePrefix = ImageProcessorConfig.Instance.RemotePrefix;
remotePrefix = ImageProcessorConfiguration.Instance.RemotePrefix;
}
if (preserveExifMetaData == null)
{
preserveExifMetaData = ImageProcessorConfig.Instance.PreserveExifMetaData;
preserveExifMetaData = ImageProcessorConfiguration.Instance.PreserveExifMetaData;
}
#if NET45
@ -453,7 +454,7 @@ namespace ImageProcessor.Web.HttpModules
imageFactory.Load(fullPath).AutoProcess().Save(cachedPath);
// Store the response type in the context for later retrieval.
context.Items[CachedResponseTypeKey] = imageFactory.MimeType;
context.Items[CachedResponseTypeKey] = imageFactory.CurrentImageFormat.MimeType;
// Ensure that the LastWriteTime property of the source and cached file match.
Tuple<DateTime, DateTime> creationAndLastWriteDateTimes = await cache.SetCachedLastWriteTimeAsync();
@ -555,7 +556,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);
}
}

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

@ -11,17 +11,10 @@
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;
using ImageProcessor.Web.Configuration;
#endregion
/// <summary>
@ -53,7 +46,7 @@ namespace ImageProcessor.Web
{
// Get a list of all graphics processors that have parsed and matched the query string.
List<IGraphicsProcessor> graphicsProcessors =
ImageProcessorConfig.Instance.GraphicsProcessors
ImageProcessorConfiguration.Instance.GraphicsProcessors
.Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue)
.OrderBy(y => y.SortOrder)
.ToList();
@ -61,70 +54,12 @@ namespace ImageProcessor.Web
// Loop through and process the image.
foreach (IGraphicsProcessor graphicsProcessor in graphicsProcessors)
{
ApplyProcessor(graphicsProcessor.ProcessImage, factory);
factory.CurrentImageFormat.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.
}
}
}
}
}
}

6
src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj

@ -51,14 +51,18 @@
<Compile Include="Caching\CacheIndexer.cs" />
<Compile Include="Config\ImageCacheSection.cs" />
<Compile Include="Config\ImageProcessingSection.cs" />
<Compile Include="Config\ImageProcessorConfig.cs" />
<Compile Include="Config\ImageProcessorConfiguration.cs" />
<Compile Include="Config\ImageSecuritySection.cs" />
<Compile Include="Helpers\ParameterParserUtilities.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\BackgroundColor.cs" />
<Compile Include="Processors\IWebGraphicsProcessor.cs" />
<Compile Include="Processors\Rotate.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

88
src/ImageProcessor.Web/NET45/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 = ParameterParserUtilities.ParseColor(match.Value);
}
index += 1;
}
}
return this.SortOrder;
}
}
}

51
src/ImageProcessor.Web/NET45/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
}
}

96
src/ImageProcessor.Web/NET45/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 = ParameterParserUtilities.ParseAngle(match.Value);
}
index += 1;
}
}
return this.SortOrder;
}
#endregion
}
}

24
src/ImageProcessor.Web/NET45/Processors/RoundedCorners.cs

@ -0,0 +1,24 @@

namespace ImageProcessor.Web.Processors
{
using System;
using System.Text.RegularExpressions;
using ImageProcessor.Processors;
/// <summary>
/// Encapsulates methods to add rounded corners to an image.
/// </summary>
public class RoundedCorners : IWebGraphicsProcessor
{
public Regex RegexPattern { get; private set; }
public int SortOrder { get; private set; }
public IGraphicsProcessor Processor { get; private set; }
public int MatchRegexIndex(string queryString)
{
throw new NotImplementedException();
}
}
}

81
src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs

@ -0,0 +1,81 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageProcessorBootstrapper.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The image processor bootstrapper.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Configuration
{
using System;
using System.Collections.Generic;
using System.Linq;
using ImageProcessor.Core.Common.Exceptions;
using ImageProcessor.Imaging.Formats;
/// <summary>
/// The image processor bootstrapper.
/// </summary>
public class ImageProcessorBootstrapper
{
/// <summary>
/// A new instance Initializes a new instance of the <see cref="ImageProcessorBootstrapper"/> class.
/// with lazy initialization.
/// </summary>
private static readonly Lazy<ImageProcessorBootstrapper> Lazy =
new Lazy<ImageProcessorBootstrapper>(() => new ImageProcessorBootstrapper());
/// <summary>
/// Prevents a default instance of the <see cref="ImageProcessorBootstrapper"/> class from being created.
/// </summary>
private ImageProcessorBootstrapper()
{
this.LoadSupportedImageFormats();
}
/// <summary>
/// Gets the current instance of the <see cref="ImageProcessorBootstrapper"/> class.
/// </summary>
public static ImageProcessorBootstrapper Instance
{
get
{
return Lazy.Value;
}
}
/// <summary>
/// Gets the supported image formats.
/// </summary>
public IEnumerable<ISupportedImageFormat> SupportedImageFormats { get; private set; }
/// <summary>
/// Creates a list, using reflection, of supported image formats that ImageProcessor can run.
/// </summary>
private void LoadSupportedImageFormats()
{
if (this.SupportedImageFormats == null)
{
try
{
Type type = typeof(ISupportedImageFormat);
List<Type> availableTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => t != null && type.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract)
.ToList();
this.SupportedImageFormats = availableTypes
.Select(x => (Activator.CreateInstance(x) as ISupportedImageFormat)).ToList();
}
catch (Exception ex)
{
throw new ImageFormatException(ex.Message, ex.InnerException);
}
}
}
}
}

39
src/ImageProcessor/Core/Common/Exceptions/ImageFormatException.cs

@ -0,0 +1,39 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageFormatException.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The exception that is thrown when loading the supported image format types has failed.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Core.Common.Exceptions
{
using System;
/// <summary>
/// The exception that is thrown when loading the supported image format types has failed.
/// </summary>
public sealed class ImageFormatException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public ImageFormatException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
public ImageFormatException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

39
src/ImageProcessor/Core/Common/Exceptions/ImageProcessingException.cs

@ -0,0 +1,39 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageProcessingException.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The exception that is thrown when processing an image has failed.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Core.Common.Exceptions
{
using System;
/// <summary>
/// The exception that is thrown when processing an image has failed.
/// </summary>
public sealed class ImageProcessingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public ImageProcessingException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
public ImageProcessingException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

2
src/ImageProcessor/Extensions/DoubleExtensions.cs → src/ImageProcessor/Core/Common/Extensions/DoubleExtensions.cs

@ -8,7 +8,7 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Extensions
namespace ImageProcessor.Core.Common.Extensions
{
/// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Double"/> class.

3
src/ImageProcessor/Extensions/ImageExtensions.cs → src/ImageProcessor/Core/Common/Extensions/ImageExtensions.cs

@ -8,12 +8,13 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Extensions
namespace ImageProcessor.Core.Common.Extensions
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using ImageProcessor.Imaging;
/// <summary>

4
src/ImageProcessor/Extensions/ImageFormatExtensions.cs → src/ImageProcessor/Core/Common/Extensions/ImageFormatExtensions.cs

@ -9,11 +9,13 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Extensions
namespace ImageProcessor.Core.Common.Extensions
{
#region
using System.Drawing.Imaging;
using System.Linq;
#endregion
/// <summary>

3
src/ImageProcessor/Extensions/ImageInfo.cs → src/ImageProcessor/Core/Common/Extensions/ImageInfo.cs

@ -9,9 +9,10 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Extensions
namespace ImageProcessor.Core.Common.Extensions
{
using System.Collections.Generic;
using ImageProcessor.Imaging;
/// <summary>

2
src/ImageProcessor/Extensions/IntegerExtensions.cs → src/ImageProcessor/Core/Common/Extensions/IntegerExtensions.cs

@ -8,7 +8,7 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Extensions
namespace ImageProcessor.Core.Common.Extensions
{
using System.Globalization;

4
src/ImageProcessor/Extensions/StringExtensions.cs → src/ImageProcessor/Core/Common/Extensions/StringExtensions.cs

@ -8,15 +8,17 @@
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Extensions
namespace ImageProcessor.Core.Common.Extensions
{
#region Using
using System;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
#endregion
/// <summary>

332
src/ImageProcessor/ImageFactory.cs

@ -14,15 +14,15 @@ namespace ImageProcessor
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Exceptions;
using ImageProcessor.Imaging;
using ImageProcessor.Imaging.Filters;
using ImageProcessor.Imaging.Formats;
using ImageProcessor.Processors;
#endregion
@ -33,24 +33,14 @@ namespace ImageProcessor
{
#region Fields
/// <summary>
/// The default quality for jpeg files.
/// </summary>
private const int DefaultJpegQuality = 90;
/// <summary>
/// The backup image format.
/// </summary>
private ImageFormat backupImageFormat;
/// <summary>
/// The memory stream for storing any input stream to prevent disposal.
/// The default quality for image files.
/// </summary>
private MemoryStream inputStream;
private const int DefaultQuality = 90;
/// <summary>
/// Whether the image is indexed.
/// The backup supported image format.
/// </summary>
private bool isIndexed;
private ISupportedImageFormat backupFormat;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
@ -122,20 +112,9 @@ namespace ImageProcessor
public bool ShouldProcess { get; private set; }
/// <summary>
/// Gets the file format of the image.
/// Gets the supported image format.
/// </summary>
public ImageFormat ImageFormat { get; private set; }
/// <summary>
/// Gets the mime type.
/// </summary>
public string MimeType
{
get
{
return this.ImageFormat.GetMimeType();
}
}
public ISupportedImageFormat CurrentImageFormat { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether to preserve exif metadata.
@ -153,9 +132,9 @@ namespace ImageProcessor
internal string OriginalExtension { get; set; }
/// <summary>
/// Gets or sets the quality of output for jpeg images as a percentile.
/// Gets or sets the memory stream for storing any input stream to prevent disposal.
/// </summary>
internal int JpegQuality { get; set; }
internal MemoryStream InputStream { get; set; }
#endregion
#region Methods
@ -170,17 +149,25 @@ namespace ImageProcessor
/// </returns>
public ImageFactory Load(MemoryStream memoryStream)
{
ISupportedImageFormat format = FormatUtilities.GetFormat(memoryStream);
if (format == null)
{
throw new ImageFormatException("Input stream is not a supported format.");
}
this.backupFormat = format;
this.CurrentImageFormat = format;
// Set our image as the memory stream value.
this.Image = Image.FromStream(memoryStream, true);
this.Image = format.Load(memoryStream);
// Store the stream so we can dispose of it later.
this.inputStream = memoryStream;
this.InputStream = memoryStream;
// Set the other properties.
this.JpegQuality = DefaultJpegQuality;
this.ImageFormat = this.Image.RawFormat;
this.backupImageFormat = this.ImageFormat;
this.isIndexed = ImageUtils.IsIndexed(this.Image);
format.Quality = DefaultQuality;
format.IsIndexed = ImageUtils.IsIndexed(this.Image);
if (this.PreserveExifData)
{
@ -222,6 +209,16 @@ namespace ImageProcessor
// Open a file stream to prevent the need for lock.
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
ISupportedImageFormat format = FormatUtilities.GetFormat(fileStream);
if (format == null)
{
throw new ImageFormatException("Input stream is not a supported format.");
}
this.backupFormat = format;
this.CurrentImageFormat = format;
MemoryStream memoryStream = new MemoryStream();
// Copy the stream.
@ -231,18 +228,16 @@ namespace ImageProcessor
fileStream.Position = memoryStream.Position = 0;
// Set our image as the memory stream value.
this.Image = Image.FromStream(memoryStream, true);
this.Image = format.Load(memoryStream);
// Store the stream so we can dispose of it later.
this.inputStream = memoryStream;
this.InputStream = memoryStream;
// Set the other properties.
this.JpegQuality = DefaultJpegQuality;
ImageFormat imageFormat = this.Image.RawFormat;
this.backupImageFormat = imageFormat;
format.Quality = DefaultQuality;
format.IsIndexed = ImageUtils.IsIndexed(this.Image);
this.OriginalExtension = Path.GetExtension(this.ImagePath);
this.ImageFormat = imageFormat;
this.isIndexed = ImageUtils.IsIndexed(this.Image);
if (this.PreserveExifData)
{
@ -271,6 +266,16 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
this.Image = image;
// Get the correct image format for the image.
using (MemoryStream stream = new MemoryStream())
{
image.Save(stream, image.RawFormat);
stream.Position = 0;
int quality = this.CurrentImageFormat.Quality;
this.CurrentImageFormat = FormatUtilities.GetFormat(stream);
this.CurrentImageFormat.Quality = quality;
}
}
return this;
@ -287,16 +292,15 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
// Set our new image as the memory stream value.
Image newImage = Image.FromStream(this.inputStream, true);
Image newImage = Image.FromStream(this.InputStream, true);
// Dispose and reassign the image.
this.Image.Dispose();
this.Image = newImage;
// Set the other properties.
this.JpegQuality = DefaultJpegQuality;
this.ImageFormat = this.backupImageFormat;
this.isIndexed = ImageUtils.IsIndexed(this.Image);
this.CurrentImageFormat = this.backupFormat;
this.CurrentImageFormat.Quality = DefaultQuality;
}
return this;
@ -312,6 +316,7 @@ namespace ImageProcessor
/// </returns>
public ImageFactory AddQueryString(string query)
{
// TODO: Remove this.
if (this.ShouldProcess)
{
this.QueryString = query;
@ -341,7 +346,7 @@ namespace ImageProcessor
}
Alpha alpha = new Alpha { DynamicParameter = percentage };
this.ApplyProcessor(alpha.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(alpha.ProcessImage, this);
}
return this;
@ -368,7 +373,7 @@ namespace ImageProcessor
}
Brightness brightness = new Brightness { DynamicParameter = percentage };
this.ApplyProcessor(brightness.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(brightness.ProcessImage, this);
}
return this;
@ -416,7 +421,7 @@ namespace ImageProcessor
}
Contrast contrast = new Contrast { DynamicParameter = percentage };
this.ApplyProcessor(contrast.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(contrast.ProcessImage, this);
}
return this;
@ -456,7 +461,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Crop crop = new Crop { DynamicParameter = cropLayer };
this.ApplyProcessor(crop.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(crop.ProcessImage, this);
}
return this;
@ -477,7 +482,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Filter filter = new Filter { DynamicParameter = filterName };
this.ApplyProcessor(filter.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(filter.ProcessImage, this);
}
return this;
@ -498,7 +503,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Filter filter = new Filter { DynamicParameter = matrixFilter };
this.ApplyProcessor(filter.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(filter.ProcessImage, this);
}
return this;
@ -522,7 +527,7 @@ namespace ImageProcessor
: RotateFlipType.RotateNoneFlipY;
Flip flip = new Flip { DynamicParameter = rotateFlipType };
this.ApplyProcessor(flip.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(flip.ProcessImage, this);
}
return this;
@ -531,18 +536,15 @@ namespace ImageProcessor
/// <summary>
/// Sets the output format of the current image to the matching <see cref="T:System.Drawing.Imaging.ImageFormat"/>.
/// </summary>
/// <param name="imageFormat">The <see cref="T:System.Drawing.Imaging.ImageFormat"/>. to set the image to.</param>
/// <param name="indexedFormat">Whether the pixel format of the image should be indexed. Used for generating Png8 images.</param>
/// <param name="format">The <see cref="ISupportedImageFormat"/>. to set the image to.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public ImageFactory Format(ImageFormat imageFormat, bool indexedFormat = false)
public ImageFactory Format(ISupportedImageFormat format)
{
if (this.ShouldProcess)
{
this.isIndexed = indexedFormat;
this.ImageFormat = imageFormat;
this.CurrentImageFormat = format;
}
return this;
@ -589,7 +591,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
GaussianBlur gaussianBlur = new GaussianBlur { DynamicParameter = gaussianLayer };
this.ApplyProcessor(gaussianBlur.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(gaussianBlur.ProcessImage, this);
}
return this;
@ -636,7 +638,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
GaussianSharpen gaussianSharpen = new GaussianSharpen { DynamicParameter = gaussianLayer };
this.ApplyProcessor(gaussianSharpen.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(gaussianSharpen.ProcessImage, this);
}
return this;
@ -656,7 +658,7 @@ namespace ImageProcessor
{
if (this.ShouldProcess)
{
this.JpegQuality = percentage;
this.CurrentImageFormat.Quality = percentage;
}
return this;
@ -701,7 +703,7 @@ namespace ImageProcessor
var resizeSettings = new Dictionary<string, string> { { "MaxWidth", resizeLayer.Size.Width.ToString("G") }, { "MaxHeight", resizeLayer.Size.Height.ToString("G") } };
Resize resize = new Resize { DynamicParameter = resizeLayer, Settings = resizeSettings };
this.ApplyProcessor(resize.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(resize.ProcessImage, this);
}
return this;
@ -710,24 +712,24 @@ namespace ImageProcessor
/// <summary>
/// Rotates the current image by the given angle.
/// </summary>
/// <param name="rotateLayer">
/// The <see cref="T:ImageProcessor.Imaging.RotateLayer"/> containing the properties to rotate the image.
/// <param name="degrees">
/// The angle at which to rotate the image in degrees.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Rotate(RotateLayer rotateLayer)
public ImageFactory Rotate(int degrees)
{
if (this.ShouldProcess)
{
// Sanitize the input.
if (rotateLayer.Angle > 360 || rotateLayer.Angle < 0)
if (degrees > 360 || degrees < 0)
{
rotateLayer.Angle = 0;
degrees = 0;
}
Rotate rotate = new Rotate { DynamicParameter = rotateLayer };
this.ApplyProcessor(rotate.ProcessImage);
Rotate rotate = new Rotate { DynamicParameter = degrees };
this.CurrentImageFormat.ApplyProcessor(rotate.ProcessImage, this);
}
return this;
@ -752,7 +754,7 @@ namespace ImageProcessor
}
RoundedCorners roundedCorners = new RoundedCorners { DynamicParameter = roundedCornerLayer };
this.ApplyProcessor(roundedCorners.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(roundedCorners.ProcessImage, this);
}
return this;
@ -779,7 +781,7 @@ namespace ImageProcessor
}
Saturation saturate = new Saturation { DynamicParameter = percentage };
this.ApplyProcessor(saturate.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(saturate.ProcessImage, this);
}
return this;
@ -799,7 +801,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Tint tint = new Tint { DynamicParameter = color };
this.ApplyProcessor(tint.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(tint.ProcessImage, this);
}
return this;
@ -816,7 +818,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Vignette vignette = new Vignette();
this.ApplyProcessor(vignette.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(vignette.ProcessImage, this);
}
return this;
@ -837,7 +839,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Watermark watermark = new Watermark { DynamicParameter = textLayer };
this.ApplyProcessor(watermark.ProcessImage);
this.CurrentImageFormat.ApplyProcessor(watermark.ProcessImage, this);
}
return this;
@ -845,7 +847,9 @@ namespace ImageProcessor
#endregion
/// <summary>
/// Saves the current image to the specified file path.
/// Saves the current image to the specified file path. If the extension does not
/// match the correct extension for the current format it will be replaced by the
/// correct default value.
/// </summary>
/// <param name="filePath">The path to save the image to.</param>
/// <returns>
@ -858,71 +862,24 @@ namespace ImageProcessor
// We need to check here if the path has an extension and remove it if so.
// This is so we can add the correct image format.
int length = filePath.LastIndexOf(".", StringComparison.Ordinal);
string extension = this.ImageFormat.GetFileExtension(this.OriginalExtension);
string extension = Path.GetExtension(filePath).TrimStart('.');
string fallback = this.CurrentImageFormat.DefaultExtension;
if (!string.IsNullOrWhiteSpace(extension))
if (extension != fallback && !this.CurrentImageFormat.FileExtensions.Contains(extension))
{
filePath = length == -1 ? filePath + extension : filePath.Substring(0, length) + extension;
filePath = length == -1
? string.Format("{0}.{1}", filePath, fallback)
: filePath.Substring(0, length + 1) + fallback;
}
// Fix the colour palette of indexed images.
this.FixIndexedPallete();
// ReSharper disable once AssignNullToNotNullAttribute
DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(filePath));
if (this.ImageFormat.Equals(ImageFormat.Jpeg))
if (!directoryInfo.Exists)
{
// Jpegs can be saved with different settings to include a quality setting for the JPEG compression.
// This improves output compression and quality.
using (EncoderParameters encoderParameters = ImageUtils.GetEncodingParameters(this.JpegQuality))
{
ImageCodecInfo imageCodecInfo =
ImageCodecInfo.GetImageEncoders()
.FirstOrDefault(ici => ici.MimeType.Equals(this.MimeType, StringComparison.OrdinalIgnoreCase));
if (imageCodecInfo != null)
{
for (int i = 0; i < 3; i++)
{
try
{
if (!directoryInfo.Exists)
{
directoryInfo.Create();
}
this.Image.Save(filePath, imageCodecInfo, encoderParameters);
break;
}
catch (Exception)
{
Thread.Sleep(200);
}
}
}
}
directoryInfo.Create();
}
else
{
for (int i = 0; i < 3; i++)
{
try
{
if (!directoryInfo.Exists)
{
directoryInfo.Create();
}
this.Image.Save(filePath, this.ImageFormat);
break;
}
catch (Exception)
{
Thread.Sleep(200);
}
}
}
this.Image = this.CurrentImageFormat.Save(filePath, this.Image);
}
return this;
@ -941,29 +898,7 @@ namespace ImageProcessor
{
if (this.ShouldProcess)
{
// Fix the colour palette of gif and png8 images.
this.FixIndexedPallete();
if (this.ImageFormat.Equals(ImageFormat.Jpeg))
{
// Jpegs can be saved with different settings to include a quality setting for the JPEG compression.
// This improves output compression and quality.
using (EncoderParameters encoderParameters = ImageUtils.GetEncodingParameters(this.JpegQuality))
{
ImageCodecInfo imageCodecInfo =
ImageCodecInfo.GetImageEncoders().FirstOrDefault(
ici => ici.MimeType.Equals(this.MimeType, StringComparison.OrdinalIgnoreCase));
if (imageCodecInfo != null)
{
this.Image.Save(memoryStream, imageCodecInfo, encoderParameters);
}
}
}
else
{
this.Image.Save(memoryStream, this.ImageFormat);
}
this.Image = this.CurrentImageFormat.Save(memoryStream, this.Image);
}
return this;
@ -1002,10 +937,10 @@ namespace ImageProcessor
if (this.Image != null)
{
// Dispose of the memory stream from Load and the image.
if (this.inputStream != null)
if (this.InputStream != null)
{
this.inputStream.Dispose();
this.inputStream = null;
this.InputStream.Dispose();
this.InputStream = null;
}
this.Image.Dispose();
@ -1019,81 +954,6 @@ namespace ImageProcessor
this.isDisposed = true;
}
#endregion
/// <summary>
/// Uses the <see cref="T:ImageProcessor.Imaging.ColorQuantizer"/>
/// to fix the color palette of gif images.
/// </summary>
private void FixIndexedPallete()
{
ImageFormat format = this.ImageFormat;
// Fix the colour palette of indexed images.
if (this.isIndexed || format.Equals(ImageFormat.Gif))
{
ImageInfo imageInfo = this.Image.GetImageInfo(format, false);
if (!imageInfo.IsAnimated)
{
this.Image = new OctreeQuantizer(255, 8).Quantize(this.Image);
}
}
}
/// <summary>
/// Applies the given processor the current image.
/// </summary>
/// <param name="processor">
/// The processor delegate.
/// </param>
private void ApplyProcessor(Func<ImageFactory, Image> processor)
{
ImageInfo imageInfo = this.Image.GetImageInfo(this.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)
{
this.Image = frame.Image;
frame.Image = quantizer.Quantize(processor.Invoke(this));
encoder.AddFrame(frame);
}
}
stream.Position = 0;
this.Image = Image.FromStream(stream);
}
else
{
this.Image = processor.Invoke(this);
}
// Set the property item information from any Exif metadata.
// We do this here so that they can be changed between processor methods.
if (this.PreserveExifData)
{
foreach (KeyValuePair<int, PropertyItem> propertItem in this.ExifPropertyItems)
{
try
{
this.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.
}
}
}
}
#endregion
}
}

24
src/ImageProcessor/ImageProcessor.csproj

@ -60,12 +60,15 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\DoubleExtensions.cs" />
<Compile Include="Extensions\ImageExtensions.cs" />
<Compile Include="Extensions\ImageFormatExtensions.cs" />
<Compile Include="Extensions\ImageInfo.cs" />
<Compile Include="Extensions\IntegerExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Configuration\ImageProcessorBootstrapper.cs" />
<Compile Include="Core\Common\Exceptions\ImageProcessingException.cs" />
<Compile Include="Core\Common\Extensions\DoubleExtensions.cs" />
<Compile Include="Core\Common\Extensions\ImageExtensions.cs" />
<Compile Include="Core\Common\Extensions\ImageFormatExtensions.cs" />
<Compile Include="Core\Common\Extensions\ImageInfo.cs" />
<Compile Include="Core\Common\Extensions\IntegerExtensions.cs" />
<Compile Include="Core\Common\Extensions\StringExtensions.cs" />
<Compile Include="Core\Common\Exceptions\ImageFormatException.cs" />
<Compile Include="ImageFactory.cs" />
<Compile Include="Imaging\AnchorPosition.cs" />
<Compile Include="Imaging\ExifPropertyTagType.cs" />
@ -75,6 +78,14 @@
<Compile Include="Imaging\ExifPropertyTag.cs" />
<Compile Include="Imaging\Filters\MatrixFilterRegexAttribute.cs" />
<Compile Include="Imaging\Filters\MatrixFilters.cs" />
<Compile Include="Imaging\Formats\BitmapFormat.cs" />
<Compile Include="Imaging\Formats\TiffFormat.cs" />
<Compile Include="Imaging\Formats\PngFormat.cs" />
<Compile Include="Imaging\Formats\GifFormat.cs" />
<Compile Include="Imaging\Formats\JpegFormat.cs" />
<Compile Include="Imaging\Formats\FormatUtilities.cs" />
<Compile Include="Imaging\Formats\FormatBase.cs" />
<Compile Include="Imaging\Formats\ISupportedImageFormat.cs" />
<Compile Include="Imaging\GaussianLayer.cs" />
<Compile Include="Imaging\GifEncoder.cs" />
<Compile Include="Imaging\GifFrame.cs" />
@ -100,6 +111,7 @@
<Compile Include="Imaging\TextLayer.cs" />
<Compile Include="Imaging\OctreeQuantizer.cs" />
<Compile Include="Processors\Alpha.cs" />
<Compile Include="Processors\BackgroundColor.cs" />
<Compile Include="Processors\GaussianBlur.cs" />
<Compile Include="Processors\Brightness.cs" />
<Compile Include="Processors\Contrast.cs" />

3
src/ImageProcessor/Imaging/Convolution.cs

@ -14,7 +14,8 @@ namespace ImageProcessor.Imaging
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Extensions;
/// <summary>
/// Provides methods for applying blurring and sharpening effects to an image..

2
src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs

@ -18,7 +18,7 @@ namespace ImageProcessor.Imaging.Filters
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Extensions;
#endregion

65
src/ImageProcessor/Imaging/Formats/BitmapFormat.cs

@ -0,0 +1,65 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BitmapFormat.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides the necessary information to support bitmap images.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System.Drawing.Imaging;
using System.Text;
/// <summary>
/// Provides the necessary information to support bitmap images.
/// </summary>
public class BitmapFormat : FormatBase
{
/// <summary>
/// Gets the file header.
/// </summary>
public override byte[] FileHeader
{
get
{
return Encoding.ASCII.GetBytes("BM");
}
}
/// <summary>
/// Gets the list of file extensions.
/// </summary>
public override string[] FileExtensions
{
get
{
return new[] { "bmp" };
}
}
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
public override string MimeType
{
get
{
return "image/bmp";
}
}
/// <summary>
/// Gets the <see cref="ImageFormat" />.
/// </summary>
public override ImageFormat ImageFormat
{
get
{
return ImageFormat.Bmp;
}
}
}
}

123
src/ImageProcessor/Imaging/Formats/FormatBase.cs

@ -0,0 +1,123 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="FormatBase.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The supported format base implement this class when building a supported format.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
/// <summary>
/// The supported format base. Implement this class when building a supported format.
/// </summary>
public abstract class FormatBase : ISupportedImageFormat
{
/// <summary>
/// Gets the file header.
/// </summary>
public abstract byte[] FileHeader { get; }
/// <summary>
/// Gets the list of file extensions.
/// </summary>
public abstract string[] FileExtensions { get; }
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
public abstract string MimeType { get; }
/// <summary>
/// Gets the default file extension.
/// </summary>
public string DefaultExtension
{
get
{
return this.MimeType.Replace("image/", string.Empty);
}
}
/// <summary>
/// Gets the file format of the image.
/// </summary>
public abstract ImageFormat ImageFormat { get; }
/// <summary>
/// Gets or sets a value indicating whether the image format is indexed.
/// </summary>
public bool IsIndexed { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image format is animated.
/// </summary>
public bool IsAnimated { get; set; }
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
public int Quality { get; set; }
/// <summary>
/// Applies the given processor the current image.
/// </summary>
/// <param name="processor">The processor delegate.</param>
/// <param name="factory">The <see cref="ImageFactory" />.</param>
public virtual void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory)
{
processor.Invoke(factory);
}
/// <summary>
/// Decodes the image to process.
/// </summary>
/// <param name="stream">
/// The <see cref="T:System.IO.stream" /> containing the image information.
/// </param>
/// <returns>
/// The the <see cref="T:System.Drawing.Image" />.
/// </returns>
public virtual Image Load(Stream stream)
{
return Image.FromStream(stream, true);
}
/// <summary>
/// Saves the current image to the specified output stream.
/// </summary>
/// <param name="memoryStream">The <see cref="T:System.IO.MemoryStream" /> to save the image information to.</param>
/// <param name="image">The <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public virtual Image Save(MemoryStream memoryStream, Image image)
{
image.Save(memoryStream, this.ImageFormat);
memoryStream.Position = 0;
return image;
}
/// <summary>
/// Saves the current image to the specified file path.
/// </summary>
/// <param name="path">The path to save the image to.</param>
/// <param name="image">The
/// <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public virtual Image Save(string path, Image image)
{
image.Save(path, this.ImageFormat);
return image;
}
}
}

88
src/ImageProcessor/Imaging/Formats/FormatUtilities.cs

@ -0,0 +1,88 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="FormatUtilities.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Utility methods for working with supported image formats.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using ImageProcessor.Configuration;
/// <summary>
/// Utility methods for working with supported image formats.
/// </summary>
public static class FormatUtilities
{
/// <summary>
/// Gets the correct <see cref="ISupportedImageFormat"/> from the given stream.
/// <see cref="http://stackoverflow.com/questions/55869/determine-file-type-of-an-image"/>
/// </summary>
/// <param name="stream">
/// The <see cref="System.IO.Stream"/> to read from.
/// </param>
/// <returns>
/// The <see cref="ISupportedImageFormat"/>.
/// </returns>
public static ISupportedImageFormat GetFormat(Stream stream)
{
IEnumerable<ISupportedImageFormat> supportedImageFormats =
ImageProcessorBootstrapper.Instance.SupportedImageFormats;
byte[] buffer = new byte[4];
stream.Read(buffer, 0, buffer.Length);
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (ISupportedImageFormat supportedImageFormat in supportedImageFormats)
{
byte[] header = supportedImageFormat.FileHeader;
if (header.SequenceEqual(buffer.Take(header.Length)))
{
stream.Position = 0;
return supportedImageFormat;
}
}
stream.Position = 0;
return null;
}
/// <summary>
/// Returns a value indicating whether the given image is indexed.
/// </summary>
/// <param name="image">
/// The <see cref="System.Drawing.Image"/> to test.
/// </param>
/// <returns>
/// The true if the image is indexed; otherwise, false.
/// </returns>
public static bool IsIndexed(Image image)
{
// Test value of flags using bitwise AND.
// ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
return (image.PixelFormat & PixelFormat.Indexed) != 0;
}
/// <summary>
/// Returns a value indicating whether the given image is indexed.
/// </summary>
/// <param name="image">
/// The <see cref="System.Drawing.Image"/> to test.
/// </param>
/// <returns>
/// The true if the image is animated; otherwise, false.
/// </returns>
public static bool IsAnimated(Image image)
{
return ImageAnimator.CanAnimate(image);
}
}
}

149
src/ImageProcessor/Imaging/Formats/GifFormat.cs

@ -0,0 +1,149 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifFormat.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides the necessary information to support gif images.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using ImageProcessor.Core.Common.Extensions;
/// <summary>
/// Provides the necessary information to support gif images.
/// </summary>
public class GifFormat : FormatBase
{
/// <summary>
/// Gets the file header.
/// </summary>
public override byte[] FileHeader
{
get
{
return Encoding.ASCII.GetBytes("GIF");
}
}
/// <summary>
/// Gets the list of file extensions.
/// </summary>
public override string[] FileExtensions
{
get
{
return new[] { "gif" };
}
}
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
public override string MimeType
{
get
{
return "image/gif";
}
}
/// <summary>
/// Gets the <see cref="ImageFormat" />.
/// </summary>
public override ImageFormat ImageFormat
{
get
{
return ImageFormat.Gif;
}
}
/// <summary>
/// Applies the given processor the current image.
/// </summary>
/// <param name="processor">The processor delegate.</param>
/// <param name="factory">The <see cref="ImageFactory" />.</param>
public override void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory)
{
ImageInfo imageInfo = factory.Image.GetImageInfo(this.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
{
base.ApplyProcessor(processor, factory);
}
}
/// <summary>
/// Saves the current image to the specified output stream.
/// </summary>
/// <param name="memoryStream">
/// The <see cref="T:System.IO.MemoryStream" /> to save the image information to.
/// </param>
/// <param name="image">The <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public override Image Save(MemoryStream memoryStream, Image image)
{
// TODO: Move this in here. It doesn't need to be anywhere else.
ImageInfo imageInfo = image.GetImageInfo(this.ImageFormat, false);
if (!imageInfo.IsAnimated)
{
image = new OctreeQuantizer(255, 8).Quantize(image);
}
return base.Save(memoryStream, image);
}
/// <summary>
/// Saves the current image to the specified file path.
/// </summary>
/// <param name="path">The path to save the image to.</param>
/// <param name="image">The
/// <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public override Image Save(string path, Image image)
{
// TODO: Move this in here. It doesn't need to be anywhere else.
ImageInfo imageInfo = image.GetImageInfo(this.ImageFormat, false);
if (!imageInfo.IsAnimated)
{
image = new OctreeQuantizer(255, 8).Quantize(image);
}
return base.Save(path, image);
}
}
}

113
src/ImageProcessor/Imaging/Formats/ISupportedImageFormat.cs

@ -0,0 +1,113 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ISupportedImageFormat.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The SupportedImageFormat interface providing information about image formats to ImageProcessor.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
/// <summary>
/// The SupportedImageFormat interface providing information about image formats to ImageProcessor.
/// </summary>
public interface ISupportedImageFormat
{
/// <summary>
/// Gets the file header.
/// </summary>
byte[] FileHeader { get; }
/// <summary>
/// Gets the list of file extensions.
/// </summary>
string[] FileExtensions { get; }
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
string MimeType { get; }
/// <summary>
/// Gets the default file extension.
/// </summary>
string DefaultExtension { get; }
/// <summary>
/// Gets the file format of the image.
/// </summary>
ImageFormat ImageFormat { get; }
/// <summary>
/// Gets or sets a value indicating whether the image format is indexed.
/// </summary>
bool IsIndexed { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image format is animated.
/// </summary>
bool IsAnimated { get; set; }
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
int Quality { get; set; }
#region Methods
/// <summary>
/// Applies the given processor the current image.
/// </summary>
/// <param name="processor">
/// The processor delegate.
/// </param>
/// <param name="factory">
/// The <see cref="ImageFactory"/>.
/// </param>
void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory);
/// <summary>
/// Loads the image to process.
/// </summary>
/// <param name="stream">
/// The <see cref="T:System.IO.Stream"/> containing the image information.
/// </param>
/// <returns>
/// The <see cref="T:System.Drawing.Image"/>.
/// </returns>
Image Load(Stream stream);
/// <summary>
/// Saves the current image to the specified output stream.
/// </summary>
/// <param name="memoryStream">
/// The <see cref="T:System.IO.MemoryStream"/> to save the image information to.
/// </param>
/// <param name="image">
/// The <see cref="T:System.Drawing.Image"/> to save.
/// </param>
/// <returns>
/// The <see cref="T:System.Drawing.Image"/>.
/// </returns>
Image Save(MemoryStream memoryStream, Image image);
/// <summary>
/// Saves the current image to the specified file path.
/// </summary>
/// <param name="path">The path to save the image to.</param>
/// <param name="image">
/// The <see cref="T:System.Drawing.Image"/> to save.
/// </param>
/// <returns>
/// The <see cref="T:System.Drawing.Image"/>.
/// </returns>
Image Save(string path, Image image);
#endregion
}
}

155
src/ImageProcessor/Imaging/Formats/JpegFormat.cs

@ -0,0 +1,155 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="JpegFormat.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides the necessary information to support jpeg images.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
/// <summary>
/// Provides the necessary information to support jpeg images.
/// </summary>
public sealed class JpegFormat : FormatBase
{
/// <summary>
/// Gets the file header.
/// </summary>
public override byte[] FileHeader
{
get
{
return new byte[] { 255, 216, 255, 224 };
}
}
/// <summary>
/// Gets the list of file extensions.
/// </summary>
public override string[] FileExtensions
{
get
{
return new[] { "jpeg", "jpg" };
}
}
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
public override string MimeType
{
get
{
return "image/jpeg";
}
}
/// <summary>
/// Gets the <see cref="ImageFormat" />.
/// </summary>
public override ImageFormat ImageFormat
{
get
{
return ImageFormat.Jpeg;
}
}
/// <summary>
/// Applies the given processor the current image.
/// </summary>
/// <param name="processor">The processor delegate.</param>
/// <param name="factory">The <see cref="ImageFactory" />.</param>
public override void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory)
{
base.ApplyProcessor(processor, 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.
}
}
}
}
/// <summary>
/// Saves the current image to the specified output stream.
/// </summary>
/// <param name="memoryStream">
/// The <see cref="T:System.IO.MemoryStream" /> to save the image information to.
/// </param>
/// <param name="image">The <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public override Image Save(MemoryStream memoryStream, Image image)
{
// Jpegs can be saved with different settings to include a quality setting for the JPEG compression.
// This improves output compression and quality.
using (EncoderParameters encoderParameters = ImageUtils.GetEncodingParameters(this.Quality))
{
ImageCodecInfo imageCodecInfo =
ImageCodecInfo.GetImageEncoders()
.FirstOrDefault(ici => ici.MimeType.Equals(this.MimeType, StringComparison.OrdinalIgnoreCase));
if (imageCodecInfo != null)
{
image.Save(memoryStream, imageCodecInfo, encoderParameters);
}
}
return image;
}
/// <summary>
/// Saves the current image to the specified file path.
/// </summary>
/// <param name="path">The path to save the image to.</param>
/// <param name="image">The
/// <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public override Image Save(string path, Image image)
{
// Jpegs can be saved with different settings to include a quality setting for the JPEG compression.
// This improves output compression and quality.
using (EncoderParameters encoderParameters = ImageUtils.GetEncodingParameters(this.Quality))
{
ImageCodecInfo imageCodecInfo =
ImageCodecInfo.GetImageEncoders()
.FirstOrDefault(ici => ici.MimeType.Equals(this.MimeType, StringComparison.OrdinalIgnoreCase));
if (imageCodecInfo != null)
{
image.Save(path, imageCodecInfo, encoderParameters);
}
}
return image;
}
}
}

103
src/ImageProcessor/Imaging/Formats/PngFormat.cs

@ -0,0 +1,103 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PngFormat.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides the necessary information to support png images.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
/// <summary>
/// Provides the necessary information to support png images.
/// </summary>
public class PngFormat : FormatBase
{
/// <summary>
/// Gets the file header.
/// </summary>
public override byte[] FileHeader
{
get
{
return new byte[] { 137, 80, 78, 71 };
}
}
/// <summary>
/// Gets the list of file extensions.
/// </summary>
public override string[] FileExtensions
{
get
{
return new[] { "png" };
}
}
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
public override string MimeType
{
get
{
return "image/png";
}
}
/// <summary>
/// Gets the <see cref="ImageFormat" />.
/// </summary>
public override ImageFormat ImageFormat
{
get
{
return ImageFormat.Png;
}
}
/// <summary>
/// Saves the current image to the specified output stream.
/// </summary>
/// <param name="memoryStream">The <see cref="T:System.IO.MemoryStream" /> to save the image information to.</param>
/// <param name="image">The <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public override Image Save(MemoryStream memoryStream, Image image)
{
if (FormatUtilities.IsIndexed(image))
{
image = new OctreeQuantizer(255, 8).Quantize(image);
}
return base.Save(memoryStream, image);
}
/// <summary>
/// Saves the current image to the specified file path.
/// </summary>
/// <param name="path">The path to save the image to.</param>
/// <param name="image">The
/// <see cref="T:System.Drawing.Image" /> to save.</param>
/// <returns>
/// The <see cref="T:System.Drawing.Image" />.
/// </returns>
public override Image Save(string path, Image image)
{
if (FormatUtilities.IsIndexed(image))
{
image = new OctreeQuantizer(255, 8).Quantize(image);
}
return base.Save(path, image);
}
}
}

96
src/ImageProcessor/Imaging/Formats/TiffFormat.cs

@ -0,0 +1,96 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TiffFormat.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides the necessary information to support tiff images.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
/// <summary>
/// Provides the necessary information to support tiff images.
/// </summary>
public class TiffFormat : FormatBase
{
/// <summary>
/// Gets the file header.
/// </summary>
public override byte[] FileHeader
{
get
{
return new byte[] { 77, 77, 42 };
}
}
/// <summary>
/// Gets the list of file extensions.
/// </summary>
public override string[] FileExtensions
{
get
{
return new[] { "tif", "tiff" };
}
}
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
public override string MimeType
{
get
{
return "image/tiff";
}
}
/// <summary>
/// Gets the <see cref="ImageFormat" />.
/// </summary>
public override ImageFormat ImageFormat
{
get
{
return ImageFormat.Tiff;
}
}
/// <summary>
/// Applies the given processor the current image.
/// </summary>
/// <param name="processor">The processor delegate.</param>
/// <param name="factory">The <see cref="ImageFactory" />.</param>
public override void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory)
{
base.ApplyProcessor(processor, 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.
}
}
}
}
}
}

74
src/ImageProcessor/Processors/BackgroundColor.cs

@ -0,0 +1,74 @@
// --------------------------------------------------------------------------------------------------------------------
// <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.Processors
{
using System.Collections.Generic;
using System.Drawing;
/// <summary>
/// Changes the background color of an image.
/// </summary>
public class BackgroundColor : IGraphicsProcessor
{
/// <summary>
/// Gets or sets the DynamicParameter.
/// </summary>
public dynamic DynamicParameter { get; set; }
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings { get; set; }
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">The the current instance of the
/// <see cref="T:ImageProcessor.ImageFactory" /> class containing
/// the image to process.</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory" /> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Image image = factory.Image;
try
{
Color backgroundColor = this.DynamicParameter;
newImage = new Bitmap(image.Width, image.Height);
// Make a graphics object from the empty bitmap.
using (Graphics graphics = Graphics.FromImage(newImage))
{
// Fill the background.
graphics.Clear(backgroundColor);
// Draw passed in image onto graphics object.
graphics.DrawImage(image, 0, 0);
}
image.Dispose();
image = newImage;
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
}
}

2
src/ImageProcessor/Processors/Crop.cs

@ -18,7 +18,7 @@ namespace ImageProcessor.Processors
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Extensions;
using ImageProcessor.Imaging;
#endregion

3
src/ImageProcessor/Processors/Format.cs

@ -146,7 +146,8 @@ namespace ImageProcessor.Processors
// Set the internal property.
factory.OriginalExtension = string.Format(".{0}", format);
factory.Format(imageFormat, isIndexed);
// TODO: Fix this.
//factory.Format(imageFormat);
return factory.Image;
}

27
src/ImageProcessor/Processors/IGraphicsProcessor.cs

@ -23,38 +23,17 @@ namespace ImageProcessor.Processors
{
#region Properties
/// <summary>
/// Gets the regular expression to search strings for.
/// Gets or sets the DynamicParameter.
/// </summary>
Regex RegexPattern { get; }
/// <summary>
/// Gets DynamicParameter.
/// </summary>
dynamic DynamicParameter { get; }
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
int SortOrder { get; }
dynamic DynamicParameter { get; set; }
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
Dictionary<string, string> Settings { get; set; }
Dictionary<string, string> Settings { get; set; }
#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);
/// <summary>
/// Processes the image.
/// </summary>

2
src/ImageProcessor/Processors/Quality.cs

@ -115,7 +115,7 @@ namespace ImageProcessor.Processors
public Image ProcessImage(ImageFactory factory)
{
// Set the internal property.
factory.JpegQuality = this.DynamicParameter;
factory.CurrentImageFormat.Quality = this.DynamicParameter;
return factory.Image;
}

3
src/ImageProcessor/Processors/Resize.cs

@ -20,7 +20,8 @@ namespace ImageProcessor.Processors
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Extensions;
using ImageProcessor.Imaging;
#endregion

150
src/ImageProcessor/Processors/Rotate.cs

@ -15,9 +15,6 @@ namespace ImageProcessor.Processors
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging;
#endregion
/// <summary>
@ -25,33 +22,7 @@ namespace ImageProcessor.Processors
/// </summary>
public class Rotate : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"rotate=((?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)|angle-(?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)[\|,]bgcolor-([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the angle attribute.
/// </summary>
private static readonly Regex AngleRegex = new Regex(@"angle-(?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the color attribute.
/// </summary>
private static readonly Regex ColorRegex = new Regex(@"bgcolor-([0-9a-fA-F]{3}){1,2}", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
#region IGraphicsProcessor members
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
@ -61,15 +32,6 @@ namespace ImageProcessor.Processors
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
@ -79,57 +41,6 @@ namespace ImageProcessor.Processors
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;
RotateLayer rotateLayer;
string toParse = match.Value;
if (toParse.Contains("bgcolor"))
{
rotateLayer = new RotateLayer(this.ParseAngle(toParse), this.ParseColor(toParse));
}
else
{
int degrees;
int.TryParse(match.Value.Split('=')[1], NumberStyles.Any, CultureInfo.InvariantCulture, out degrees);
rotateLayer = new RotateLayer(degrees);
}
this.DynamicParameter = rotateLayer;
}
index += 1;
}
}
return this.SortOrder;
}
/// <summary>
/// Processes the image.
/// </summary>
@ -147,16 +58,14 @@ namespace ImageProcessor.Processors
try
{
RotateLayer rotateLayer = this.DynamicParameter;
int angle = rotateLayer.Angle;
Color backgroundColor = rotateLayer.BackgroundColor;
int angle = this.DynamicParameter;
// Center of the image
float rotateAtX = Math.Abs(image.Width / 2);
float rotateAtY = Math.Abs(image.Height / 2);
// Create a rotated image.
newImage = this.RotateImage(image, rotateAtX, rotateAtY, angle, backgroundColor);
newImage = this.RotateImage(image, rotateAtX, rotateAtY, angle);
image.Dispose();
image = newImage;
@ -181,12 +90,11 @@ namespace ImageProcessor.Processors
/// <param name="rotateAtX">The horizontal pixel coordinate at which to rotate the image.</param>
/// <param name="rotateAtY">The vertical pixel coordinate at which to rotate the image.</param>
/// <param name="angle">The angle in degrees at which to rotate the image.</param>
/// <param name="backgroundColor">The background color to fill an image with.</param>
/// <returns>The image rotated to the given angle at the given position.</returns>
/// <remarks>
/// Based on <see cref="http://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations?msg=4155374#xx4155374xx"/>
/// </remarks>
private Bitmap RotateImage(Image image, float rotateAtX, float rotateAtY, float angle, Color backgroundColor)
private Bitmap RotateImage(Image image, float rotateAtX, float rotateAtY, float angle)
{
int width, height, x, y;
@ -242,14 +150,9 @@ namespace ImageProcessor.Processors
{
// Reduce the jagged edge.
graphics.SmoothingMode = SmoothingMode.HighQuality;
// Contrary to everything I have read bicubic is producing the best results.
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighSpeed;
// Fill the background.
graphics.Clear(backgroundColor);
graphics.CompositingQuality = CompositingQuality.HighQuality;
// Put the rotation point in the "center" of the image
graphics.TranslateTransform(rotateAtX + x, rotateAtY + y);
@ -266,49 +169,6 @@ namespace ImageProcessor.Processors
return newImage;
}
/// <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>
private int ParseAngle(string input)
{
foreach (Match match in AngleRegex.Matches(input))
{
// Split on angle-
int angle;
int.TryParse(match.Value.Split('-')[1], 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>
private Color ParseColor(string input)
{
foreach (Match match in ColorRegex.Matches(input))
{
// split on color-hex
return ColorTranslator.FromHtml("#" + match.Value.Split('-')[1]);
}
return Color.Transparent;
}
#endregion
}
}

3
src/ImageProcessor/Processors/Tint.cs

@ -10,13 +10,12 @@
namespace ImageProcessor.Processors
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Extensions;
/// <summary>
/// Tints an image with the given colour.

2
src/ImageProcessor/Processors/Watermark.cs

@ -20,7 +20,7 @@ namespace ImageProcessor.Processors
using System.Linq;
using System.Text.RegularExpressions;
using ImageProcessor.Extensions;
using ImageProcessor.Core.Common.Extensions;
using ImageProcessor.Imaging;
#endregion

2
src/ImageProcessor/Settings.StyleCop

@ -2,6 +2,7 @@
<GlobalSettings>
<CollectionProperty Name="RecognizedWords">
<Value>behaviour</Value>
<Value>bootstrapper</Value>
<Value>chrominance</Value>
<Value>colour</Value>
<Value>enum</Value>
@ -9,6 +10,7 @@
<Value>halftoning</Value>
<Value>lomograph</Value>
<Value>octree</Value>
<Value>png</Value>
<Value>quantizer</Value>
</CollectionProperty>
</GlobalSettings>

4
src/ImageProcessorConsole/Program.cs

@ -38,9 +38,7 @@ namespace ImageProcessorConsole
// Load, resize, set the format and quality and save an image.
imageFactory.Load(inStream)
.Constrain(size)
.Tint(Color.FromArgb(255, 106, 166, 204))
.Format(format)
.Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name)));
.Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name.TrimEnd(".gif".ToCharArray()) + ".jpg")));
}
}
}

2
src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id

@ -1 +1 @@
17e154964bfb4da80c1e0aec623cd2486d493b47
30ec5c05548fd350f9b7c699715848b9fbfb8ca9

2
src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id

@ -1 +1 @@
069f8472ed7b83ea57f4cf511f83396e1a0b8877
23a1c81a2d1422076373796e0c47f5d968c56d0b
Loading…
Cancel
Save