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