diff --git a/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs b/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
index 8052e0540..5b0562dc6 100644
--- a/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs
+++ b/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
///
/// The maximum number of days to cache files on the system for.
///
- internal static readonly int MaxFileCachedDuration = ImageProcessorConfig.Instance.MaxCacheDays;
+ internal static readonly int MaxFileCachedDuration = ImageProcessorConfiguration.Instance.MaxCacheDays;
///
/// 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.
///
private static readonly string AbsoluteCachePath =
- HostingEnvironment.MapPath(ImageProcessorConfig.Instance.VirtualCachePath);
+ HostingEnvironment.MapPath(ImageProcessorConfiguration.Instance.VirtualCachePath);
///
/// The request for the image.
diff --git a/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs b/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs
index 6bda16a44..27e5320c3 100644
--- a/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs
+++ b/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs
@@ -8,14 +8,14 @@
//
// --------------------------------------------------------------------------------------------------------------------
-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
diff --git a/src/ImageProcessor.Web/NET45/Config/ImageProcessingSection.cs b/src/ImageProcessor.Web/NET45/Config/ImageProcessingSection.cs
index d1018ba89..3942280f6 100644
--- a/src/ImageProcessor.Web/NET45/Config/ImageProcessingSection.cs
+++ b/src/ImageProcessor.Web/NET45/Config/ImageProcessingSection.cs
@@ -9,7 +9,7 @@
//
// --------------------------------------------------------------------------------------------------------------------
-namespace ImageProcessor.Web.Config
+namespace ImageProcessor.Web.Configuration
{
#region Using
using System.Configuration;
diff --git a/src/ImageProcessor.Web/NET45/Config/ImageProcessorConfig.cs b/src/ImageProcessor.Web/NET45/Config/ImageProcessorConfiguration.cs
similarity index 97%
rename from src/ImageProcessor.Web/NET45/Config/ImageProcessorConfig.cs
rename to src/ImageProcessor.Web/NET45/Config/ImageProcessorConfiguration.cs
index c85c25039..2bcf73d78 100644
--- a/src/ImageProcessor.Web/NET45/Config/ImageProcessorConfig.cs
+++ b/src/ImageProcessor.Web/NET45/Config/ImageProcessorConfiguration.cs
@@ -8,7 +8,7 @@
//
//
// --------------------------------------------------------------------------------------------------------------------
-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.
///
///
- public sealed class ImageProcessorConfig
+ public sealed class ImageProcessorConfiguration
{
#region Fields
///
/// A new instance Initializes a new instance of the class.
/// with lazy initialization.
///
- private static readonly Lazy Lazy =
- new Lazy(() => new ImageProcessorConfig());
+ private static readonly Lazy Lazy =
+ new Lazy(() => new ImageProcessorConfiguration());
///
/// A collection of the elements
@@ -67,7 +67,7 @@ namespace ImageProcessor.Web.Config
///
/// Prevents a default instance of the class from being created.
///
- private ImageProcessorConfig()
+ private ImageProcessorConfiguration()
{
this.LoadGraphicsProcessors();
}
@@ -77,7 +77,7 @@ namespace ImageProcessor.Web.Config
///
/// Gets the current instance of the class.
///
- public static ImageProcessorConfig Instance
+ public static ImageProcessorConfiguration Instance
{
get
{
diff --git a/src/ImageProcessor.Web/NET45/Config/ImageSecuritySection.cs b/src/ImageProcessor.Web/NET45/Config/ImageSecuritySection.cs
index 824cf8277..29079fdc2 100644
--- a/src/ImageProcessor.Web/NET45/Config/ImageSecuritySection.cs
+++ b/src/ImageProcessor.Web/NET45/Config/ImageSecuritySection.cs
@@ -8,7 +8,7 @@
//
// --------------------------------------------------------------------------------------------------------------------
-namespace ImageProcessor.Web.Config
+namespace ImageProcessor.Web.Configuration
{
#region Using
using System;
diff --git a/src/ImageProcessor.Web/NET45/Helpers/ParameterParserUtilities.cs b/src/ImageProcessor.Web/NET45/Helpers/ParameterParserUtilities.cs
new file mode 100644
index 000000000..eb6af4357
--- /dev/null
+++ b/src/ImageProcessor.Web/NET45/Helpers/ParameterParserUtilities.cs
@@ -0,0 +1,90 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Encapsulates methods to correctly parse querystring parameters.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Helpers
+{
+ using System.Drawing;
+ using System.Globalization;
+ using System.Text.RegularExpressions;
+ using ImageProcessor.Core.Common.Extensions;
+
+ ///
+ /// Encapsulates methods to correctly parse querystring parameters.
+ ///
+ public static class ParameterParserUtilities
+ {
+ ///
+ /// The regular expression to search strings for colors.
+ ///
+ private static readonly Regex ColorRegex = new Regex(@"(bgcolor|color)(=|-)(\d+,\d+,\d+,\d+|([0-9a-fA-F]{3}){1,2})", RegexOptions.Compiled);
+
+ ///
+ /// The regular expression to search strings for angles.
+ ///
+ private static readonly Regex AngleRegex = new Regex(@"(rotate|angle)(=|-)(?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)", RegexOptions.Compiled);
+
+ ///
+ /// Returns the correct containing the angle for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct containing the angle for the given string.
+ ///
+ 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;
+ }
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct
+ ///
+ 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;
+ }
+ }
+}
diff --git a/src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs b/src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs
index 05c2f9753..1fa38568c 100644
--- a/src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs
+++ b/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
///
@@ -48,27 +48,27 @@ namespace ImageProcessor.Web.Helpers
///
/// The white-list of url[s] from which to download remote files.
///
- public static readonly ImageSecuritySection.SafeUrl[] RemoteFileWhiteListExtensions = ImageProcessorConfig.Instance.RemoteFileWhiteListExtensions;
+ public static readonly ImageSecuritySection.SafeUrl[] RemoteFileWhiteListExtensions = ImageProcessorConfiguration.Instance.RemoteFileWhiteListExtensions;
///
/// The white-list of url[s] from which to download remote files.
///
- private static readonly Uri[] RemoteFileWhiteList = ImageProcessorConfig.Instance.RemoteFileWhiteList;
+ private static readonly Uri[] RemoteFileWhiteList = ImageProcessorConfiguration.Instance.RemoteFileWhiteList;
///
/// The length of time, in milliseconds, that a remote file download attempt can last before timing out.
///
- private static readonly int TimeoutMilliseconds = ImageProcessorConfig.Instance.Timeout;
+ private static readonly int TimeoutMilliseconds = ImageProcessorConfiguration.Instance.Timeout;
///
/// The maximum size, in bytes, that a remote file download attempt can download.
///
- private static readonly int MaxBytes = ImageProcessorConfig.Instance.MaxBytes;
+ private static readonly int MaxBytes = ImageProcessorConfiguration.Instance.MaxBytes;
///
/// Whether to allow remote downloads.
///
- private static readonly bool AllowRemoteDownloads = ImageProcessorConfig.Instance.AllowRemoteDownloads;
+ private static readonly bool AllowRemoteDownloads = ImageProcessorConfiguration.Instance.AllowRemoteDownloads;
///
/// Whether this RemoteFile instance is ignoring remote download rules set in the current application
diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
index efcbdc2ed..6d10a49ee 100644
--- a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
+++ b/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 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);
}
}
diff --git a/src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs b/src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs
index 0c8788cd9..c677149fb 100644
--- a/src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs
+++ b/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
///
@@ -53,7 +46,7 @@ namespace ImageProcessor.Web
{
// Get a list of all graphics processors that have parsed and matched the query string.
List 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;
}
-
- ///
- /// Processes the image.
- ///
- ///
- /// The processor.
- ///
- ///
- /// The factory.
- ///
- private static void ApplyProcessor(Func 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 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.
- }
- }
- }
- }
}
}
diff --git a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj
index 23cc45e96..bdf8ba8dc 100644
--- a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj
+++ b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj
@@ -51,14 +51,18 @@
-
+
+
+
+
+
diff --git a/src/ImageProcessor.Web/NET45/Processors/BackgroundColor.cs b/src/ImageProcessor.Web/NET45/Processors/BackgroundColor.cs
new file mode 100644
index 000000000..7fc0a49a5
--- /dev/null
+++ b/src/ImageProcessor.Web/NET45/Processors/BackgroundColor.cs
@@ -0,0 +1,88 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Changes the background color of an image.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Processors
+{
+ using System.Text.RegularExpressions;
+ using ImageProcessor.Processors;
+ using ImageProcessor.Web.Helpers;
+
+ ///
+ /// Changes the background color of an image.
+ ///
+ public class BackgroundColor : IWebGraphicsProcessor
+ {
+ ///
+ /// The regular expression to search strings for.
+ ///
+ private static readonly Regex QueryRegex = new Regex(@"bgcolor(=|-)[^&|,]*", RegexOptions.Compiled);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BackgroundColor()
+ {
+ this.Processor = new ImageProcessor.Processors.BackgroundColor();
+ }
+
+ ///
+ /// Gets the regular expression to search strings for.
+ ///
+ public Regex RegexPattern
+ {
+ get
+ {
+ return QueryRegex;
+ }
+ }
+
+ ///
+ /// Gets the order in which this processor is to be used in a chain.
+ ///
+ public int SortOrder { get; private set; }
+
+ ///
+ /// Gets the associated graphics processor.
+ ///
+ public IGraphicsProcessor Processor { get; private set; }
+
+ ///
+ /// The position in the original string where the first character of the captured substring was found.
+ ///
+ /// The query string to search.
+ ///
+ /// The zero-based starting position in the original string where the captured substring was found.
+ ///
+ public int MatchRegexIndex(string queryString)
+ {
+ int index = 0;
+
+ // Set the sort order to max to allow filtering.
+ this.SortOrder = int.MaxValue;
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor.Web/NET45/Processors/IWebGraphicsProcessor.cs b/src/ImageProcessor.Web/NET45/Processors/IWebGraphicsProcessor.cs
new file mode 100644
index 000000000..b15d86d30
--- /dev/null
+++ b/src/ImageProcessor.Web/NET45/Processors/IWebGraphicsProcessor.cs
@@ -0,0 +1,51 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Defines properties and methods for ImageProcessor.Web Plugins.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Processors
+{
+ using System.Text.RegularExpressions;
+ using ImageProcessor.Processors;
+
+ ///
+ /// Defines properties and methods for ImageProcessor.Web Plugins.
+ ///
+ public interface IWebGraphicsProcessor
+ {
+ #region Properties
+ ///
+ /// Gets the regular expression to search strings for.
+ ///
+ Regex RegexPattern { get; }
+
+ ///
+ /// Gets the order in which this processor is to be used in a chain.
+ ///
+ int SortOrder { get; }
+
+ ///
+ /// Gets the associated graphics processor.
+ ///
+ IGraphicsProcessor Processor { get; }
+ #endregion
+
+ #region Methods
+ ///
+ /// The position in the original string where the first character of the captured substring was found.
+ ///
+ ///
+ /// The query string to search.
+ ///
+ ///
+ /// The zero-based starting position in the original string where the captured substring was found.
+ ///
+ int MatchRegexIndex(string queryString);
+ #endregion
+ }
+}
diff --git a/src/ImageProcessor.Web/NET45/Processors/Rotate.cs b/src/ImageProcessor.Web/NET45/Processors/Rotate.cs
new file mode 100644
index 000000000..58375b179
--- /dev/null
+++ b/src/ImageProcessor.Web/NET45/Processors/Rotate.cs
@@ -0,0 +1,96 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Encapsulates methods to rotate an image.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Processors
+{
+ using System.Text.RegularExpressions;
+ using ImageProcessor.Processors;
+ using ImageProcessor.Web.Helpers;
+
+ ///
+ /// Encapsulates methods to rotate an image.
+ ///
+ public class Rotate : IWebGraphicsProcessor
+ {
+ ///
+ /// The regular expression to search strings for.
+ ///
+ private static readonly Regex QueryRegex = new Regex(@"(rotate|angle)(=|-)[^&|,]*", RegexOptions.Compiled);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Rotate()
+ {
+ this.Processor = new ImageProcessor.Processors.Rotate();
+ }
+
+ #region IGraphicsProcessor Members
+ ///
+ /// Gets the regular expression to search strings for.
+ ///
+ public Regex RegexPattern
+ {
+ get
+ {
+ return QueryRegex;
+ }
+ }
+
+ ///
+ /// Gets the order in which this processor is to be used in a chain.
+ ///
+ public int SortOrder
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the associated graphics processor.
+ ///
+ public IGraphicsProcessor Processor { get; private set; }
+
+ ///
+ /// The position in the original string where the first character of the captured substring was found.
+ ///
+ ///
+ /// The query string to search.
+ ///
+ ///
+ /// The zero-based starting position in the original string where the captured substring was found.
+ ///
+ public int MatchRegexIndex(string queryString)
+ {
+ int index = 0;
+
+ // Set the sort order to max to allow filtering.
+ this.SortOrder = int.MaxValue;
+
+ 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
+ }
+}
diff --git a/src/ImageProcessor.Web/NET45/Processors/RoundedCorners.cs b/src/ImageProcessor.Web/NET45/Processors/RoundedCorners.cs
new file mode 100644
index 000000000..d09d19bba
--- /dev/null
+++ b/src/ImageProcessor.Web/NET45/Processors/RoundedCorners.cs
@@ -0,0 +1,24 @@
+
+namespace ImageProcessor.Web.Processors
+{
+ using System;
+ using System.Text.RegularExpressions;
+ using ImageProcessor.Processors;
+
+ ///
+ /// Encapsulates methods to add rounded corners to an image.
+ ///
+ 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();
+ }
+ }
+}
diff --git a/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs b/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs
new file mode 100644
index 000000000..594ce5185
--- /dev/null
+++ b/src/ImageProcessor/Configuration/ImageProcessorBootstrapper.cs
@@ -0,0 +1,81 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The image processor bootstrapper.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Configuration
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using ImageProcessor.Core.Common.Exceptions;
+ using ImageProcessor.Imaging.Formats;
+
+ ///
+ /// The image processor bootstrapper.
+ ///
+ public class ImageProcessorBootstrapper
+ {
+ ///
+ /// A new instance Initializes a new instance of the class.
+ /// with lazy initialization.
+ ///
+ private static readonly Lazy Lazy =
+ new Lazy(() => new ImageProcessorBootstrapper());
+
+ ///
+ /// Prevents a default instance of the class from being created.
+ ///
+ private ImageProcessorBootstrapper()
+ {
+ this.LoadSupportedImageFormats();
+ }
+
+ ///
+ /// Gets the current instance of the class.
+ ///
+ public static ImageProcessorBootstrapper Instance
+ {
+ get
+ {
+ return Lazy.Value;
+ }
+ }
+
+ ///
+ /// Gets the supported image formats.
+ ///
+ public IEnumerable SupportedImageFormats { get; private set; }
+
+ ///
+ /// Creates a list, using reflection, of supported image formats that ImageProcessor can run.
+ ///
+ private void LoadSupportedImageFormats()
+ {
+ if (this.SupportedImageFormats == null)
+ {
+ try
+ {
+ Type type = typeof(ISupportedImageFormat);
+ List 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageProcessor/Core/Common/Exceptions/ImageFormatException.cs b/src/ImageProcessor/Core/Common/Exceptions/ImageFormatException.cs
new file mode 100644
index 000000000..0469e33d4
--- /dev/null
+++ b/src/ImageProcessor/Core/Common/Exceptions/ImageFormatException.cs
@@ -0,0 +1,39 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The exception that is thrown when loading the supported image format types has failed.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Core.Common.Exceptions
+{
+ using System;
+
+ ///
+ /// The exception that is thrown when loading the supported image format types has failed.
+ ///
+ public sealed class ImageFormatException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message that describes the error.
+ public ImageFormatException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The error message that explains the reason for the exception.
+ /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.
+ public ImageFormatException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/src/ImageProcessor/Core/Common/Exceptions/ImageProcessingException.cs b/src/ImageProcessor/Core/Common/Exceptions/ImageProcessingException.cs
new file mode 100644
index 000000000..db1adf2a6
--- /dev/null
+++ b/src/ImageProcessor/Core/Common/Exceptions/ImageProcessingException.cs
@@ -0,0 +1,39 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The exception that is thrown when processing an image has failed.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Core.Common.Exceptions
+{
+ using System;
+
+ ///
+ /// The exception that is thrown when processing an image has failed.
+ ///
+ public sealed class ImageProcessingException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message that describes the error.
+ public ImageProcessingException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The error message that explains the reason for the exception.
+ /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.
+ public ImageProcessingException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/src/ImageProcessor/Extensions/DoubleExtensions.cs b/src/ImageProcessor/Core/Common/Extensions/DoubleExtensions.cs
similarity index 96%
rename from src/ImageProcessor/Extensions/DoubleExtensions.cs
rename to src/ImageProcessor/Core/Common/Extensions/DoubleExtensions.cs
index a21817df1..a8980c5c6 100644
--- a/src/ImageProcessor/Extensions/DoubleExtensions.cs
+++ b/src/ImageProcessor/Core/Common/Extensions/DoubleExtensions.cs
@@ -8,7 +8,7 @@
//
// --------------------------------------------------------------------------------------------------------------------
-namespace ImageProcessor.Extensions
+namespace ImageProcessor.Core.Common.Extensions
{
///
/// Encapsulates a series of time saving extension methods to the class.
diff --git a/src/ImageProcessor/Extensions/ImageExtensions.cs b/src/ImageProcessor/Core/Common/Extensions/ImageExtensions.cs
similarity index 98%
rename from src/ImageProcessor/Extensions/ImageExtensions.cs
rename to src/ImageProcessor/Core/Common/Extensions/ImageExtensions.cs
index a4134016e..cff34a9f8 100644
--- a/src/ImageProcessor/Extensions/ImageExtensions.cs
+++ b/src/ImageProcessor/Core/Common/Extensions/ImageExtensions.cs
@@ -8,12 +8,13 @@
//
// --------------------------------------------------------------------------------------------------------------------
-namespace ImageProcessor.Extensions
+namespace ImageProcessor.Core.Common.Extensions
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
+
using ImageProcessor.Imaging;
///
diff --git a/src/ImageProcessor/Extensions/ImageFormatExtensions.cs b/src/ImageProcessor/Core/Common/Extensions/ImageFormatExtensions.cs
similarity index 98%
rename from src/ImageProcessor/Extensions/ImageFormatExtensions.cs
rename to src/ImageProcessor/Core/Common/Extensions/ImageFormatExtensions.cs
index 036a4153e..dcdaa4550 100644
--- a/src/ImageProcessor/Extensions/ImageFormatExtensions.cs
+++ b/src/ImageProcessor/Core/Common/Extensions/ImageFormatExtensions.cs
@@ -9,11 +9,13 @@
//
// --------------------------------------------------------------------------------------------------------------------
-namespace ImageProcessor.Extensions
+namespace ImageProcessor.Core.Common.Extensions
{
#region
+
using System.Drawing.Imaging;
using System.Linq;
+
#endregion
///
diff --git a/src/ImageProcessor/Extensions/ImageInfo.cs b/src/ImageProcessor/Core/Common/Extensions/ImageInfo.cs
similarity index 97%
rename from src/ImageProcessor/Extensions/ImageInfo.cs
rename to src/ImageProcessor/Core/Common/Extensions/ImageInfo.cs
index 720eb5a20..41ef6f5b0 100644
--- a/src/ImageProcessor/Extensions/ImageInfo.cs
+++ b/src/ImageProcessor/Core/Common/Extensions/ImageInfo.cs
@@ -9,9 +9,10 @@
//
// --------------------------------------------------------------------------------------------------------------------
-namespace ImageProcessor.Extensions
+namespace ImageProcessor.Core.Common.Extensions
{
using System.Collections.Generic;
+
using ImageProcessor.Imaging;
///
diff --git a/src/ImageProcessor/Extensions/IntegerExtensions.cs b/src/ImageProcessor/Core/Common/Extensions/IntegerExtensions.cs
similarity index 97%
rename from src/ImageProcessor/Extensions/IntegerExtensions.cs
rename to src/ImageProcessor/Core/Common/Extensions/IntegerExtensions.cs
index d969e780e..c4aa90bd8 100644
--- a/src/ImageProcessor/Extensions/IntegerExtensions.cs
+++ b/src/ImageProcessor/Core/Common/Extensions/IntegerExtensions.cs
@@ -8,7 +8,7 @@
//
// --------------------------------------------------------------------------------------------------------------------
-namespace ImageProcessor.Extensions
+namespace ImageProcessor.Core.Common.Extensions
{
using System.Globalization;
diff --git a/src/ImageProcessor/Extensions/StringExtensions.cs b/src/ImageProcessor/Core/Common/Extensions/StringExtensions.cs
similarity index 99%
rename from src/ImageProcessor/Extensions/StringExtensions.cs
rename to src/ImageProcessor/Core/Common/Extensions/StringExtensions.cs
index 318146a9b..a60590f66 100644
--- a/src/ImageProcessor/Extensions/StringExtensions.cs
+++ b/src/ImageProcessor/Core/Common/Extensions/StringExtensions.cs
@@ -8,15 +8,17 @@
//
// --------------------------------------------------------------------------------------------------------------------
-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
///
diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs
index b88c3d3bf..8d9b34f75 100644
--- a/src/ImageProcessor/ImageFactory.cs
+++ b/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
///
- /// The default quality for jpeg files.
- ///
- private const int DefaultJpegQuality = 90;
-
- ///
- /// The backup image format.
- ///
- private ImageFormat backupImageFormat;
-
- ///
- /// The memory stream for storing any input stream to prevent disposal.
+ /// The default quality for image files.
///
- private MemoryStream inputStream;
+ private const int DefaultQuality = 90;
///
- /// Whether the image is indexed.
+ /// The backup supported image format.
///
- private bool isIndexed;
+ private ISupportedImageFormat backupFormat;
///
/// 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; }
///
- /// Gets the file format of the image.
+ /// Gets the supported image format.
///
- public ImageFormat ImageFormat { get; private set; }
-
- ///
- /// Gets the mime type.
- ///
- public string MimeType
- {
- get
- {
- return this.ImageFormat.GetMimeType();
- }
- }
+ public ISupportedImageFormat CurrentImageFormat { get; private set; }
///
/// Gets or sets a value indicating whether to preserve exif metadata.
@@ -153,9 +132,9 @@ namespace ImageProcessor
internal string OriginalExtension { get; set; }
///
- /// 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.
///
- internal int JpegQuality { get; set; }
+ internal MemoryStream InputStream { get; set; }
#endregion
#region Methods
@@ -170,17 +149,25 @@ namespace ImageProcessor
///
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
///
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
///
/// Sets the output format of the current image to the matching .
///
- /// The . to set the image to.
- /// Whether the pixel format of the image should be indexed. Used for generating Png8 images.
+ /// The . to set the image to.
///
/// The current instance of the class.
///
- [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 { { "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
///
/// Rotates the current image by the given angle.
///
- ///
- /// The containing the properties to rotate the image.
+ ///
+ /// The angle at which to rotate the image in degrees.
///
///
/// The current instance of the class.
///
- 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
///
- /// 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.
///
/// The path to save the image to.
///
@@ -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
-
- ///
- /// Uses the
- /// to fix the color palette of gif images.
- ///
- 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);
- }
- }
- }
-
- ///
- /// Applies the given processor the current image.
- ///
- ///
- /// The processor delegate.
- ///
- private void ApplyProcessor(Func 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 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
}
}
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index 54aa058bb..68f6e06bd 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -60,12 +60,15 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -75,6 +78,14 @@
+
+
+
+
+
+
+
+
@@ -100,6 +111,7 @@
+
diff --git a/src/ImageProcessor/Imaging/Convolution.cs b/src/ImageProcessor/Imaging/Convolution.cs
index be3f585e9..1d8088970 100644
--- a/src/ImageProcessor/Imaging/Convolution.cs
+++ b/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;
///
/// Provides methods for applying blurring and sharpening effects to an image..
diff --git a/src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs
index 2f46693cb..7c7feab83 100644
--- a/src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs
+++ b/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
diff --git a/src/ImageProcessor/Imaging/Formats/BitmapFormat.cs b/src/ImageProcessor/Imaging/Formats/BitmapFormat.cs
new file mode 100644
index 000000000..b971f276e
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/BitmapFormat.cs
@@ -0,0 +1,65 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Provides the necessary information to support bitmap images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System.Drawing.Imaging;
+ using System.Text;
+
+ ///
+ /// Provides the necessary information to support bitmap images.
+ ///
+ public class BitmapFormat : FormatBase
+ {
+ ///
+ /// Gets the file header.
+ ///
+ public override byte[] FileHeader
+ {
+ get
+ {
+ return Encoding.ASCII.GetBytes("BM");
+ }
+ }
+
+ ///
+ /// Gets the list of file extensions.
+ ///
+ public override string[] FileExtensions
+ {
+ get
+ {
+ return new[] { "bmp" };
+ }
+ }
+
+ ///
+ /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
+ ///
+ public override string MimeType
+ {
+ get
+ {
+ return "image/bmp";
+ }
+ }
+
+ ///
+ /// Gets the .
+ ///
+ public override ImageFormat ImageFormat
+ {
+ get
+ {
+ return ImageFormat.Bmp;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Imaging/Formats/FormatBase.cs b/src/ImageProcessor/Imaging/Formats/FormatBase.cs
new file mode 100644
index 000000000..79c90a12b
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/FormatBase.cs
@@ -0,0 +1,123 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The supported format base implement this class when building a supported format.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+ using System.IO;
+
+ ///
+ /// The supported format base. Implement this class when building a supported format.
+ ///
+ public abstract class FormatBase : ISupportedImageFormat
+ {
+ ///
+ /// Gets the file header.
+ ///
+ public abstract byte[] FileHeader { get; }
+
+ ///
+ /// Gets the list of file extensions.
+ ///
+ public abstract string[] FileExtensions { get; }
+
+ ///
+ /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
+ ///
+ public abstract string MimeType { get; }
+
+ ///
+ /// Gets the default file extension.
+ ///
+ public string DefaultExtension
+ {
+ get
+ {
+ return this.MimeType.Replace("image/", string.Empty);
+ }
+ }
+
+ ///
+ /// Gets the file format of the image.
+ ///
+ public abstract ImageFormat ImageFormat { get; }
+
+ ///
+ /// Gets or sets a value indicating whether the image format is indexed.
+ ///
+ public bool IsIndexed { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the image format is animated.
+ ///
+ public bool IsAnimated { get; set; }
+
+ ///
+ /// Gets or sets the quality of output for images.
+ ///
+ public int Quality { get; set; }
+
+ ///
+ /// Applies the given processor the current image.
+ ///
+ /// The processor delegate.
+ /// The .
+ public virtual void ApplyProcessor(Func processor, ImageFactory factory)
+ {
+ processor.Invoke(factory);
+ }
+
+ ///
+ /// Decodes the image to process.
+ ///
+ ///
+ /// The containing the image information.
+ ///
+ ///
+ /// The the .
+ ///
+ public virtual Image Load(Stream stream)
+ {
+ return Image.FromStream(stream, true);
+ }
+
+ ///
+ /// Saves the current image to the specified output stream.
+ ///
+ /// The to save the image information to.
+ /// The to save.
+ ///
+ /// The .
+ ///
+ public virtual Image Save(MemoryStream memoryStream, Image image)
+ {
+ image.Save(memoryStream, this.ImageFormat);
+ memoryStream.Position = 0;
+ return image;
+ }
+
+ ///
+ /// Saves the current image to the specified file path.
+ ///
+ /// The path to save the image to.
+ /// The
+ /// to save.
+ ///
+ /// The .
+ ///
+ public virtual Image Save(string path, Image image)
+ {
+ image.Save(path, this.ImageFormat);
+ return image;
+ }
+ }
+}
diff --git a/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs b/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs
new file mode 100644
index 000000000..d3b6f9c56
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs
@@ -0,0 +1,88 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Utility methods for working with supported image formats.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+ using System.IO;
+ using System.Linq;
+ using ImageProcessor.Configuration;
+
+ ///
+ /// Utility methods for working with supported image formats.
+ ///
+ public static class FormatUtilities
+ {
+ ///
+ /// Gets the correct from the given stream.
+ ///
+ ///
+ ///
+ /// The to read from.
+ ///
+ ///
+ /// The .
+ ///
+ public static ISupportedImageFormat GetFormat(Stream stream)
+ {
+ IEnumerable 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;
+ }
+
+ ///
+ /// Returns a value indicating whether the given image is indexed.
+ ///
+ ///
+ /// The to test.
+ ///
+ ///
+ /// The true if the image is indexed; otherwise, false.
+ ///
+ public static bool IsIndexed(Image image)
+ {
+ // Test value of flags using bitwise AND.
+ // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
+ return (image.PixelFormat & PixelFormat.Indexed) != 0;
+ }
+
+ ///
+ /// Returns a value indicating whether the given image is indexed.
+ ///
+ ///
+ /// The to test.
+ ///
+ ///
+ /// The true if the image is animated; otherwise, false.
+ ///
+ public static bool IsAnimated(Image image)
+ {
+ return ImageAnimator.CanAnimate(image);
+ }
+ }
+}
diff --git a/src/ImageProcessor/Imaging/Formats/GifFormat.cs b/src/ImageProcessor/Imaging/Formats/GifFormat.cs
new file mode 100644
index 000000000..b2b3cb413
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/GifFormat.cs
@@ -0,0 +1,149 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Provides the necessary information to support gif images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+ using System.IO;
+ using System.Text;
+ using ImageProcessor.Core.Common.Extensions;
+
+ ///
+ /// Provides the necessary information to support gif images.
+ ///
+ public class GifFormat : FormatBase
+ {
+ ///
+ /// Gets the file header.
+ ///
+ public override byte[] FileHeader
+ {
+ get
+ {
+ return Encoding.ASCII.GetBytes("GIF");
+ }
+ }
+
+ ///
+ /// Gets the list of file extensions.
+ ///
+ public override string[] FileExtensions
+ {
+ get
+ {
+ return new[] { "gif" };
+ }
+ }
+
+ ///
+ /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
+ ///
+ public override string MimeType
+ {
+ get
+ {
+ return "image/gif";
+ }
+ }
+
+ ///
+ /// Gets the .
+ ///
+ public override ImageFormat ImageFormat
+ {
+ get
+ {
+ return ImageFormat.Gif;
+ }
+ }
+
+ ///
+ /// Applies the given processor the current image.
+ ///
+ /// The processor delegate.
+ /// The .
+ public override void ApplyProcessor(Func 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);
+ }
+ }
+
+ ///
+ /// Saves the current image to the specified output stream.
+ ///
+ ///
+ /// The to save the image information to.
+ ///
+ /// The to save.
+ ///
+ /// The .
+ ///
+ 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);
+ }
+
+ ///
+ /// Saves the current image to the specified file path.
+ ///
+ /// The path to save the image to.
+ /// The
+ /// to save.
+ ///
+ /// The .
+ ///
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Imaging/Formats/ISupportedImageFormat.cs b/src/ImageProcessor/Imaging/Formats/ISupportedImageFormat.cs
new file mode 100644
index 000000000..8ad4e0763
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/ISupportedImageFormat.cs
@@ -0,0 +1,113 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The SupportedImageFormat interface providing information about image formats to ImageProcessor.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+ using System.IO;
+
+ ///
+ /// The SupportedImageFormat interface providing information about image formats to ImageProcessor.
+ ///
+ public interface ISupportedImageFormat
+ {
+ ///
+ /// Gets the file header.
+ ///
+ byte[] FileHeader { get; }
+
+ ///
+ /// Gets the list of file extensions.
+ ///
+ string[] FileExtensions { get; }
+
+ ///
+ /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
+ ///
+ string MimeType { get; }
+
+ ///
+ /// Gets the default file extension.
+ ///
+ string DefaultExtension { get; }
+
+ ///
+ /// Gets the file format of the image.
+ ///
+ ImageFormat ImageFormat { get; }
+
+ ///
+ /// Gets or sets a value indicating whether the image format is indexed.
+ ///
+ bool IsIndexed { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the image format is animated.
+ ///
+ bool IsAnimated { get; set; }
+
+ ///
+ /// Gets or sets the quality of output for images.
+ ///
+ int Quality { get; set; }
+
+ #region Methods
+ ///
+ /// Applies the given processor the current image.
+ ///
+ ///
+ /// The processor delegate.
+ ///
+ ///
+ /// The .
+ ///
+ void ApplyProcessor(Func processor, ImageFactory factory);
+
+ ///
+ /// Loads the image to process.
+ ///
+ ///
+ /// The containing the image information.
+ ///
+ ///
+ /// The .
+ ///
+ Image Load(Stream stream);
+
+ ///
+ /// Saves the current image to the specified output stream.
+ ///
+ ///
+ /// The to save the image information to.
+ ///
+ ///
+ /// The to save.
+ ///
+ ///
+ /// The .
+ ///
+ Image Save(MemoryStream memoryStream, Image image);
+
+ ///
+ /// Saves the current image to the specified file path.
+ ///
+ /// The path to save the image to.
+ ///
+ /// The to save.
+ ///
+ ///
+ /// The .
+ ///
+ Image Save(string path, Image image);
+ #endregion
+ }
+}
diff --git a/src/ImageProcessor/Imaging/Formats/JpegFormat.cs b/src/ImageProcessor/Imaging/Formats/JpegFormat.cs
new file mode 100644
index 000000000..761088ddc
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/JpegFormat.cs
@@ -0,0 +1,155 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Provides the necessary information to support jpeg images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+ using System.IO;
+ using System.Linq;
+
+ ///
+ /// Provides the necessary information to support jpeg images.
+ ///
+ public sealed class JpegFormat : FormatBase
+ {
+ ///
+ /// Gets the file header.
+ ///
+ public override byte[] FileHeader
+ {
+ get
+ {
+ return new byte[] { 255, 216, 255, 224 };
+ }
+ }
+
+ ///
+ /// Gets the list of file extensions.
+ ///
+ public override string[] FileExtensions
+ {
+ get
+ {
+ return new[] { "jpeg", "jpg" };
+ }
+ }
+
+ ///
+ /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
+ ///
+ public override string MimeType
+ {
+ get
+ {
+ return "image/jpeg";
+ }
+ }
+
+ ///
+ /// Gets the .
+ ///
+ public override ImageFormat ImageFormat
+ {
+ get
+ {
+ return ImageFormat.Jpeg;
+ }
+ }
+
+ ///
+ /// Applies the given processor the current image.
+ ///
+ /// The processor delegate.
+ /// The .
+ public override void ApplyProcessor(Func 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 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.
+ }
+ }
+ }
+ }
+
+ ///
+ /// Saves the current image to the specified output stream.
+ ///
+ ///
+ /// The to save the image information to.
+ ///
+ /// The to save.
+ ///
+ /// The .
+ ///
+ 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;
+ }
+
+ ///
+ /// Saves the current image to the specified file path.
+ ///
+ /// The path to save the image to.
+ /// The
+ /// to save.
+ ///
+ /// The .
+ ///
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Imaging/Formats/PngFormat.cs b/src/ImageProcessor/Imaging/Formats/PngFormat.cs
new file mode 100644
index 000000000..da6b4dc5f
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/PngFormat.cs
@@ -0,0 +1,103 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Provides the necessary information to support png images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System.Drawing;
+ using System.Drawing.Imaging;
+ using System.IO;
+
+ ///
+ /// Provides the necessary information to support png images.
+ ///
+ public class PngFormat : FormatBase
+ {
+ ///
+ /// Gets the file header.
+ ///
+ public override byte[] FileHeader
+ {
+ get
+ {
+ return new byte[] { 137, 80, 78, 71 };
+ }
+ }
+
+ ///
+ /// Gets the list of file extensions.
+ ///
+ public override string[] FileExtensions
+ {
+ get
+ {
+ return new[] { "png" };
+ }
+ }
+
+ ///
+ /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
+ ///
+ public override string MimeType
+ {
+ get
+ {
+ return "image/png";
+ }
+ }
+
+ ///
+ /// Gets the .
+ ///
+ public override ImageFormat ImageFormat
+ {
+ get
+ {
+ return ImageFormat.Png;
+ }
+ }
+
+ ///
+ /// Saves the current image to the specified output stream.
+ ///
+ /// The to save the image information to.
+ /// The to save.
+ ///
+ /// The .
+ ///
+ public override Image Save(MemoryStream memoryStream, Image image)
+ {
+ if (FormatUtilities.IsIndexed(image))
+ {
+ image = new OctreeQuantizer(255, 8).Quantize(image);
+ }
+
+ return base.Save(memoryStream, image);
+ }
+
+ ///
+ /// Saves the current image to the specified file path.
+ ///
+ /// The path to save the image to.
+ /// The
+ /// to save.
+ ///
+ /// The .
+ ///
+ public override Image Save(string path, Image image)
+ {
+ if (FormatUtilities.IsIndexed(image))
+ {
+ image = new OctreeQuantizer(255, 8).Quantize(image);
+ }
+
+ return base.Save(path, image);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Imaging/Formats/TiffFormat.cs b/src/ImageProcessor/Imaging/Formats/TiffFormat.cs
new file mode 100644
index 000000000..226c0efab
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Formats/TiffFormat.cs
@@ -0,0 +1,96 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Provides the necessary information to support tiff images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Formats
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+
+ ///
+ /// Provides the necessary information to support tiff images.
+ ///
+ public class TiffFormat : FormatBase
+ {
+ ///
+ /// Gets the file header.
+ ///
+ public override byte[] FileHeader
+ {
+ get
+ {
+ return new byte[] { 77, 77, 42 };
+ }
+ }
+
+ ///
+ /// Gets the list of file extensions.
+ ///
+ public override string[] FileExtensions
+ {
+ get
+ {
+ return new[] { "tif", "tiff" };
+ }
+ }
+
+ ///
+ /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
+ ///
+ public override string MimeType
+ {
+ get
+ {
+ return "image/tiff";
+ }
+ }
+
+ ///
+ /// Gets the .
+ ///
+ public override ImageFormat ImageFormat
+ {
+ get
+ {
+ return ImageFormat.Tiff;
+ }
+ }
+
+ ///
+ /// Applies the given processor the current image.
+ ///
+ /// The processor delegate.
+ /// The .
+ public override void ApplyProcessor(Func 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 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.
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Processors/BackgroundColor.cs b/src/ImageProcessor/Processors/BackgroundColor.cs
new file mode 100644
index 000000000..357b08014
--- /dev/null
+++ b/src/ImageProcessor/Processors/BackgroundColor.cs
@@ -0,0 +1,74 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Changes the background color of an image.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Processors
+{
+ using System.Collections.Generic;
+ using System.Drawing;
+
+ ///
+ /// Changes the background color of an image.
+ ///
+ public class BackgroundColor : IGraphicsProcessor
+ {
+ ///
+ /// Gets or sets the DynamicParameter.
+ ///
+ public dynamic DynamicParameter { get; set; }
+
+ ///
+ /// Gets or sets any additional settings required by the processor.
+ ///
+ public Dictionary Settings { get; set; }
+
+ ///
+ /// Processes the image.
+ ///
+ /// The the current instance of the
+ /// class containing
+ /// the image to process.
+ ///
+ /// The processed image from the current instance of the class.
+ ///
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Processors/Crop.cs b/src/ImageProcessor/Processors/Crop.cs
index fa4f20ccf..0124b0f61 100644
--- a/src/ImageProcessor/Processors/Crop.cs
+++ b/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
diff --git a/src/ImageProcessor/Processors/Format.cs b/src/ImageProcessor/Processors/Format.cs
index 97173b836..4879c9d11 100644
--- a/src/ImageProcessor/Processors/Format.cs
+++ b/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;
}
diff --git a/src/ImageProcessor/Processors/IGraphicsProcessor.cs b/src/ImageProcessor/Processors/IGraphicsProcessor.cs
index 148c7a7e4..a3c06c050 100644
--- a/src/ImageProcessor/Processors/IGraphicsProcessor.cs
+++ b/src/ImageProcessor/Processors/IGraphicsProcessor.cs
@@ -23,38 +23,17 @@ namespace ImageProcessor.Processors
{
#region Properties
///
- /// Gets the regular expression to search strings for.
+ /// Gets or sets the DynamicParameter.
///
- Regex RegexPattern { get; }
-
- ///
- /// Gets DynamicParameter.
- ///
- dynamic DynamicParameter { get; }
-
- ///
- /// Gets the order in which this processor is to be used in a chain.
- ///
- int SortOrder { get; }
+ dynamic DynamicParameter { get; set; }
///
/// Gets or sets any additional settings required by the processor.
///
- Dictionary Settings { get; set; }
+ Dictionary Settings { get; set; }
#endregion
#region Methods
- ///
- /// The position in the original string where the first character of the captured substring was found.
- ///
- ///
- /// The query string to search.
- ///
- ///
- /// The zero-based starting position in the original string where the captured substring was found.
- ///
- int MatchRegexIndex(string queryString);
-
///
/// Processes the image.
///
diff --git a/src/ImageProcessor/Processors/Quality.cs b/src/ImageProcessor/Processors/Quality.cs
index 97b4b7651..21995cd3c 100644
--- a/src/ImageProcessor/Processors/Quality.cs
+++ b/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;
}
diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs
index 3a95203f7..072848ee6 100644
--- a/src/ImageProcessor/Processors/Resize.cs
+++ b/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
diff --git a/src/ImageProcessor/Processors/Rotate.cs b/src/ImageProcessor/Processors/Rotate.cs
index da3e85e63..1ce5fd416 100644
--- a/src/ImageProcessor/Processors/Rotate.cs
+++ b/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
///
@@ -25,33 +22,7 @@ namespace ImageProcessor.Processors
///
public class Rotate : IGraphicsProcessor
{
- ///
- /// The regular expression to search strings for.
- ///
- 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);
-
- ///
- /// The regular expression to search strings for the angle attribute.
- ///
- private static readonly Regex AngleRegex = new Regex(@"angle-(?:3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)", RegexOptions.Compiled);
-
- ///
- /// The regular expression to search strings for the color attribute.
- ///
- private static readonly Regex ColorRegex = new Regex(@"bgcolor-([0-9a-fA-F]{3}){1,2}", RegexOptions.Compiled);
-
- #region IGraphicsProcessor Members
- ///
- /// Gets the regular expression to search strings for.
- ///
- public Regex RegexPattern
- {
- get
- {
- return QueryRegex;
- }
- }
-
+ #region IGraphicsProcessor members
///
/// Gets or sets DynamicParameter.
///
@@ -61,15 +32,6 @@ namespace ImageProcessor.Processors
set;
}
- ///
- /// Gets the order in which this processor is to be used in a chain.
- ///
- public int SortOrder
- {
- get;
- private set;
- }
-
///
/// Gets or sets any additional settings required by the processor.
///
@@ -79,57 +41,6 @@ namespace ImageProcessor.Processors
set;
}
- ///
- /// The position in the original string where the first character of the captured substring was found.
- ///
- ///
- /// The query string to search.
- ///
- ///
- /// The zero-based starting position in the original string where the captured substring was found.
- ///
- public int MatchRegexIndex(string queryString)
- {
- int index = 0;
-
- // Set the sort order to max to allow filtering.
- this.SortOrder = int.MaxValue;
-
- 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;
- }
-
///
/// Processes the image.
///
@@ -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
/// The horizontal pixel coordinate at which to rotate the image.
/// The vertical pixel coordinate at which to rotate the image.
/// The angle in degrees at which to rotate the image.
- /// The background color to fill an image with.
/// The image rotated to the given angle at the given position.
///
/// Based on
///
- 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;
}
-
- ///
- /// Returns the correct containing the angle for the given string.
- ///
- ///
- /// The input string containing the value to parse.
- ///
- ///
- /// The correct containing the angle for the given string.
- ///
- 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;
- }
-
- ///
- /// Returns the correct for the given string.
- ///
- ///
- /// The input string containing the value to parse.
- ///
- ///
- /// The correct
- ///
- 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
}
}
diff --git a/src/ImageProcessor/Processors/Tint.cs b/src/ImageProcessor/Processors/Tint.cs
index 37341552e..166789f63 100644
--- a/src/ImageProcessor/Processors/Tint.cs
+++ b/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;
///
/// Tints an image with the given colour.
diff --git a/src/ImageProcessor/Processors/Watermark.cs b/src/ImageProcessor/Processors/Watermark.cs
index 586f04404..e911a443f 100644
--- a/src/ImageProcessor/Processors/Watermark.cs
+++ b/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
diff --git a/src/ImageProcessor/Settings.StyleCop b/src/ImageProcessor/Settings.StyleCop
index ceca99cde..6f5d9d4ab 100644
--- a/src/ImageProcessor/Settings.StyleCop
+++ b/src/ImageProcessor/Settings.StyleCop
@@ -2,6 +2,7 @@
behaviour
+ bootstrapper
chrominance
colour
enum
@@ -9,6 +10,7 @@
halftoning
lomograph
octree
+ png
quantizer
diff --git a/src/ImageProcessorConsole/Program.cs b/src/ImageProcessorConsole/Program.cs
index 070afe2d8..fd5492fa8 100644
--- a/src/ImageProcessorConsole/Program.cs
+++ b/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")));
}
}
}
diff --git a/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id b/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id
index 0d2234d4e..71ce555c1 100644
--- a/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id
+++ b/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id
@@ -1 +1 @@
-17e154964bfb4da80c1e0aec623cd2486d493b47
\ No newline at end of file
+30ec5c05548fd350f9b7c699715848b9fbfb8ca9
\ No newline at end of file
diff --git a/src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id b/src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id
index e57ebffe4..4487aede0 100644
--- a/src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id
+++ b/src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id
@@ -1 +1 @@
-069f8472ed7b83ea57f4cf511f83396e1a0b8877
\ No newline at end of file
+23a1c81a2d1422076373796e0c47f5d968c56d0b
\ No newline at end of file