diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs index 90c366db7..6644284c9 100644 --- a/src/ImageProcessor/ImageFactory.cs +++ b/src/ImageProcessor/ImageFactory.cs @@ -431,6 +431,34 @@ namespace ImageProcessor return this; } + /// + /// Changes the saturation of the current image. + /// + /// + /// The percentage by which to alter the images saturation. + /// Any integer between -100 and 100. + /// + /// + /// The current instance of the class. + /// + public ImageFactory Saturate(int percentage) + { + if (this.ShouldProcess) + { + // Sanitize the input. + if (percentage > 100 || percentage < -100) + { + percentage = 0; + } + + Saturate saturate = new Saturate { DynamicParameter = percentage }; + + this.Image = saturate.ProcessImage(this); + } + + return this; + } + /// /// Adds a vignette image effect to the current image. /// @@ -499,7 +527,9 @@ namespace ImageProcessor ImageCodecInfo.GetImageEncoders().FirstOrDefault( ici => ici.MimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase)); +// ReSharper disable AssignNullToNotNullAttribute this.Image.Save(filePath, imageCodecInfo, encoderParameters); +// ReSharper restore AssignNullToNotNullAttribute } } else diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 204c0a126..3041c3f54 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -112,6 +112,7 @@ + diff --git a/src/ImageProcessor/Processors/IGraphicsProcessor.cs b/src/ImageProcessor/Processors/IGraphicsProcessor.cs index 83b0e5ce4..96c6525d3 100644 --- a/src/ImageProcessor/Processors/IGraphicsProcessor.cs +++ b/src/ImageProcessor/Processors/IGraphicsProcessor.cs @@ -8,7 +8,6 @@ namespace ImageProcessor.Processors { #region Using - using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; diff --git a/src/ImageProcessor/Processors/Saturate.cs b/src/ImageProcessor/Processors/Saturate.cs new file mode 100644 index 000000000..c3f566a38 --- /dev/null +++ b/src/ImageProcessor/Processors/Saturate.cs @@ -0,0 +1,219 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Processors +{ + #region Using + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Imaging; + using System.Text.RegularExpressions; + using ImageProcessor.Helpers.Extensions; + #endregion + + /// + /// Encapsulates methods to change the saturation component of the image. + /// + /// + /// + /// + public class Saturate : IGraphicsProcessor + { + /// + /// The regular expression to search strings for. + /// + /// + private static readonly Regex QueryRegex = new Regex(@"saturate=(-|)(?:100|[1-9]?[0-9])", RegexOptions.Compiled); + + #region IGraphicsProcessor Members + /// + /// Gets the name. + /// + public string Name + { + get + { + return "Saturate"; + } + } + + /// + /// Gets the description. + /// + public string Description + { + get + { + return "Changes the the saturation component of the image."; + } + } + + /// + /// Gets the regular expression to search strings for. + /// + public Regex RegexPattern + { + get + { + return QueryRegex; + } + } + + /// + /// Gets or sets DynamicParameter. + /// + public dynamic DynamicParameter + { + get; + 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. + /// + public Dictionary Settings + { + get; + 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; + int percentage = match.Value.ToIntegerArray()[0]; + + this.DynamicParameter = percentage; + } + + index += 1; + } + } + + return this.SortOrder; + } + + /// + /// 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 + { + float saturationFactor = (float)this.DynamicParameter / 100; + + // Stop at -1 to prevent inversion. + saturationFactor++; + + // The matrix is set up to "shear" the colour space using the following set of values. + // Note that each colour component has an effective luminance which contributes to the + // overall brightness of the pixel. + float saturationComplement = 1.0f - saturationFactor; + float saturationComplementR = 0.3086f * saturationComplement; + float saturationComplementG = 0.6094f * saturationComplement; + float saturationComplementB = 0.0820f * saturationComplement; + + // Dont use an object initializer here. + newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); + newImage.Tag = image.Tag; + + ColorMatrix colorMatrix = + new ColorMatrix( + new float[][] + { + new float[] + { + saturationComplementR + saturationFactor, saturationComplementR, + saturationComplementR, 0, 0 + }, + new float[] + { + saturationComplementG, saturationComplementG + saturationFactor, + saturationComplementG, 0, 0 + }, + new float[] + { + saturationComplementB, saturationComplementB, + saturationComplementB + saturationFactor, 0, 0 + }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }); + + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes imageAttributes = new ImageAttributes()) + { + imageAttributes.SetColorMatrix(colorMatrix); + + graphics.DrawImage( + image, + new Rectangle(0, 0, image.Width, image.Height), + 0, + 0, + image.Width, + image.Height, + GraphicsUnit.Pixel, + imageAttributes); + + image.Dispose(); + image = newImage; + } + } + } + catch + { + if (newImage != null) + { + newImage.Dispose(); + } + } + + return image; + } + #endregion + } +}