diff --git a/src/ImageProcessor.Web/ImageFactoryExtensions.cs b/src/ImageProcessor.Web/ImageFactoryExtensions.cs index cf2eb7d49..396a60a7f 100644 --- a/src/ImageProcessor.Web/ImageFactoryExtensions.cs +++ b/src/ImageProcessor.Web/ImageFactoryExtensions.cs @@ -34,8 +34,10 @@ namespace ImageProcessor.Web { // Get a list of all graphics processors that have parsed and matched the querystring. List list = - ImageProcessorConfig.Instance.GraphicsProcessors.Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue).OrderBy( - y => y.SortOrder).ToList(); + ImageProcessorConfig.Instance.GraphicsProcessors + .Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue) + .OrderBy(y => y.SortOrder) + .ToList(); // Loop through and process the image. foreach (IGraphicsProcessor graphicsProcessor in list) diff --git a/src/ImageProcessor/Helpers/Extensions/EnumExtensions.cs b/src/ImageProcessor/Helpers/Extensions/EnumExtensions.cs index f9a771c70..9d40047f2 100644 --- a/src/ImageProcessor/Helpers/Extensions/EnumExtensions.cs +++ b/src/ImageProcessor/Helpers/Extensions/EnumExtensions.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // TODO: Update copyright text. // // ----------------------------------------------------------------------- diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs index 718bd7e15..9acd8e135 100644 --- a/src/ImageProcessor/ImageFactory.cs +++ b/src/ImageProcessor/ImageFactory.cs @@ -20,7 +20,6 @@ namespace ImageProcessor /// /// Encapsulates methods for processing image files. - /// http://csharpindepth.com/Articles/General/Singleton.aspx /// public class ImageFactory : IDisposable { @@ -177,7 +176,6 @@ namespace ImageProcessor } #region Manipulation - /// /// Adds a querystring to the image factory to allow autoprocessing of remote files. /// @@ -206,7 +204,7 @@ namespace ImageProcessor { if (this.ShouldProcess) { - var alpha = new Alpha { DynamicParameter = percentage }; + Alpha alpha = new Alpha { DynamicParameter = percentage }; this.Image = alpha.ProcessImage(this); } @@ -227,7 +225,7 @@ namespace ImageProcessor { if (this.ShouldProcess) { - var crop = new Crop { DynamicParameter = rectangle }; + Crop crop = new Crop { DynamicParameter = rectangle }; this.Image = crop.ProcessImage(this); } @@ -248,7 +246,7 @@ namespace ImageProcessor { if (this.ShouldProcess) { - var filter = new Filter { DynamicParameter = filterName }; + Filter filter = new Filter { DynamicParameter = filterName }; this.Image = filter.ProcessImage(this); } @@ -304,7 +302,7 @@ namespace ImageProcessor { var resizeSettings = new Dictionary { { "MaxWidth", width.ToString("G") }, { "MaxHeight", height.ToString("G") } }; - var resize = new Resize { DynamicParameter = new Size(width, height), Settings = resizeSettings }; + Resize resize = new Resize { DynamicParameter = new Size(width, height), Settings = resizeSettings }; this.Image = resize.ProcessImage(this); } @@ -322,13 +320,34 @@ namespace ImageProcessor { if (this.ShouldProcess) { - var vignette = new Vignette(); + Vignette vignette = new Vignette(); this.Image = vignette.ProcessImage(this); } return this; } + + /// + /// Adds a text based watermark to the image + /// + /// + /// The text layer containing the properties necessary to add the text based watermark to the image. + /// + /// + /// The current instance of the class. + /// + public ImageFactory Watermark(TextLayer textLayer) + { + if (this.ShouldProcess) + { + Watermark watermark = new Watermark { DynamicParameter = textLayer }; + + this.Image = watermark.ProcessImage(this); + } + + return this; + } #endregion /// @@ -449,36 +468,6 @@ namespace ImageProcessor } #endregion - /// - /// Saves the current image to the specified file path and resets any internal parameters. - /// - /// The path to save the image to. - private void SaveFile(string filePath) - { - // Fix the colour palette of gif images. - this.FixGifs(); - - if (this.ImageFormat == 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("image/jpeg", StringComparison.OrdinalIgnoreCase)); - - if (imageCodecInfo != null) - { - this.Image.Save(filePath, imageCodecInfo, encoderParameters); - } - } - } - else - { - this.Image.Save(filePath, this.ImageFormat); - } - } - /// /// Uses the /// to fix the colour palette of gif images. diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 65709a2e3..8bab589e6 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -60,11 +60,11 @@ - + @@ -73,6 +73,7 @@ + diff --git a/src/ImageProcessor/ImageProcessor.vsdoc b/src/ImageProcessor/ImageProcessor.vsdoc index 75458c7ac..1607ec517 100644 --- a/src/ImageProcessor/ImageProcessor.vsdoc +++ b/src/ImageProcessor/ImageProcessor.vsdoc @@ -1,103 +1,105 @@ - - - - default - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - normal - yes - ImageProcessor Reference - imageprocessor_reference - vsdocman_escaped_]_]_> - - - - placeholder - no - - e3cba20ec62e422b8bb39e6be7ca2142 - - - - - - -]]> - - - - - - - - - - - - - - - - - - - + + + + default + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + normal + yes + ImageProcessor Reference + imageprocessor_reference + vsdocman_escaped_]_]_> + + + + placeholder + no + + e3cba20ec62e422b8bb39e6be7ca2142 + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ImageProcessor/Imaging/PaletteQuantizer.cs b/src/ImageProcessor/Imaging/PaletteQuantizer.cs deleted file mode 100644 index b9d929fd7..000000000 --- a/src/ImageProcessor/Imaging/PaletteQuantizer.cs +++ /dev/null @@ -1,138 +0,0 @@ -// ----------------------------------------------------------------------- -// -// TODO: Update copyright text. -// -// ----------------------------------------------------------------------- - -namespace ImageProcessor.Imaging -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Drawing; - using System.Drawing.Imaging; - using System.Linq; - using System.Text; - - /// - /// Encapsulates methods to calculate the colour palette if an image. - /// http://codebetter.com/brendantompkins/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness/ - /// - internal class PaletteQuantizer : Quantizer - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The color palette to quantize to - /// - /// - /// Palette quantization only requires a single quantization step - /// - public PaletteQuantizer(ArrayList palette) - : base(true) - { - _colorMap = new Hashtable(); - - _colors = new Color[palette.Count]; - palette.CopyTo(_colors); - } - - /// - /// Override this to process the pixel in the second pass of the algorithm - /// - /// The pixel to quantize - /// The quantized value - protected override byte QuantizePixel(Color32 pixel) - { - byte colorIndex = 0; - int colorHash = pixel.ARGB; - - // Check if the color is in the lookup table - if (_colorMap.ContainsKey(colorHash)) - { - colorIndex = (byte)_colorMap[colorHash]; - } - else - { - // Not found - loop through the palette and find the nearest match. - // Firstly check the alpha value - if 0, lookup the transparent color - if (0 == pixel.Alpha) - { - // Transparent. Lookup the first color with an alpha value of 0 - for (int index = 0; index < _colors.Length; index++) - { - if (0 == _colors[index].A) - { - colorIndex = (byte)index; - break; - } - } - } - else - { - // Not transparent... - int leastDistance = int.MaxValue; - int red = pixel.Red; - int green = pixel.Green; - int blue = pixel.Blue; - - // Loop through the entire palette, looking for the closest color match - for (int index = 0; index < _colors.Length; index++) - { - Color paletteColor = _colors[index]; - - int redDistance = paletteColor.R - red; - int greenDistance = paletteColor.G - green; - int blueDistance = paletteColor.B - blue; - - int distance = (redDistance * redDistance) + - (greenDistance * greenDistance) + - (blueDistance * blueDistance); - - if (distance < leastDistance) - { - colorIndex = (byte)index; - leastDistance = distance; - - // And if it's an exact match, exit the loop - if (0 == distance) - { - break; - } - } - } - } - - // Now I have the color, pop it into the hashtable for next time - _colorMap.Add(colorHash, colorIndex); - } - - return colorIndex; - } - - /// - /// Retrieve the palette for the quantized image - /// - /// Any old palette, this is overrwritten - /// The new color palette - protected override ColorPalette GetPalette(ColorPalette palette) - { - for (int index = 0; index < _colors.Length; index++) - { - palette.Entries[index] = _colors[index]; - } - return palette; - } - - /// - /// Lookup table for colors - /// - private Hashtable _colorMap; - - /// - /// List of all colors in the palette - /// - protected Color[] _colors; - } -} diff --git a/src/ImageProcessor/Imaging/TextLayer.cs b/src/ImageProcessor/Imaging/TextLayer.cs new file mode 100644 index 000000000..785daac0f --- /dev/null +++ b/src/ImageProcessor/Imaging/TextLayer.cs @@ -0,0 +1,113 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Imaging +{ + #region Using + + using System; + using System.Drawing; + #endregion + + /// + /// Enacapsulates the properties required to add a leyer of text to an image. + /// + public class TextLayer + { + #region Fields + /// + /// The colour to render the text. + /// + private Color textColor = Color.Black; + + /// + /// The opacity at which to render the text. + /// + private int opacity = 100; + + /// + /// The font style to render the text. + /// + private FontStyle fontStyle = FontStyle.Bold; + + /// + /// The font size to render the text. + /// + private int fontSize = 48; + + /// + /// The position to start creating the text from. + /// + private Point position = Point.Empty; + #endregion + + #region Properties + /// + /// Gets or sets Text. + /// + public string Text { get; set; } + + /// + /// Gets or sets TextColor. + /// + public Color TextColor + { + get { return this.textColor; } + set { this.textColor = value; } + } + + /// + /// Gets or sets the name of the font. + /// + public string Font { get; set; } + + /// + /// Gets or sets the size of the font in pixels. + /// + public int FontSize + { + get { return this.fontSize; } + set { this.fontSize = value; } + } + + /// + /// Gets or sets the FontStyle. + /// + public FontStyle Style + { + get { return this.fontStyle; } + set { this.fontStyle = value; } + } + + /// + /// Gets or sets the Opacity. + /// + public int Opacity + { + get + { + int alpha = (int)Math.Ceiling((this.opacity / 100d) * 255); + + return alpha < 255 ? alpha : 255; + } + + set + { + this.opacity = value; + } + } + + /// + /// Gets or sets Position. + /// + public Point Position + { + get { return this.position; } + set { this.position = value; } + } + #endregion + } +} diff --git a/src/ImageProcessor/Processors/Alpha.cs b/src/ImageProcessor/Processors/Alpha.cs index 87d2783b4..c6f76bc91 100644 --- a/src/ImageProcessor/Processors/Alpha.cs +++ b/src/ImageProcessor/Processors/Alpha.cs @@ -141,7 +141,9 @@ namespace ImageProcessor.Processors { int alphaPercent = this.DynamicParameter; - newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb) { Tag = image.Tag }; + // Dont use an object initializer here. + newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); + newImage.Tag = image.Tag; ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.Matrix00 = colorMatrix.Matrix11 = colorMatrix.Matrix22 = colorMatrix.Matrix44 = 1; diff --git a/src/ImageProcessor/Processors/Copy of Watermark.cs b/src/ImageProcessor/Processors/Copy of Watermark.cs new file mode 100644 index 000000000..1cf8fccc6 --- /dev/null +++ b/src/ImageProcessor/Processors/Copy of Watermark.cs @@ -0,0 +1,332 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Processors +{ + #region Using + using System.Collections.Generic; + using System.Drawing; + using System.Text.RegularExpressions; + using ImageProcessor.Helpers.Extensions; + using ImageProcessor.Imaging; + #endregion + + /// + /// Encapsulates methods to change the alpha component of the image to effect its transparency. + /// + public class Watermark : IGraphicsProcessor + { + /// + /// The regular expression to search strings for. + /// http://stackoverflow.com/questions/11263949/optimize-a-variable-regex + /// + private static readonly Regex QueryRegex = new Regex(@"watermark=(text-\w+\|position-\d+-\d+(\|color-([0-9a-fA-F]{3}){1,2}(\|size-\d+(\|style-(bold|italic|regular|strikeout|underline))?)?)?|\w+)", RegexOptions.Compiled); + + #region IGraphicsProcessor Members + /// + /// Gets the name. + /// + public string Name + { + get + { + return "Watermark"; + } + } + + /// + /// Gets the description. + /// + public string Description + { + get + { + return "Adds a watermark containing text to 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; + + TextLayer textLayer = new TextLayer(); + + // Split the string up. + string[] firstPass = match.Value.Split('=')[1].Split('|'); + + switch (firstPass.Length) + { + case 1: + textLayer.Text = firstPass[0]; + break; + case 2: + textLayer.Text = firstPass[0]; + textLayer.Position = this.GetPosition(firstPass[1]); + break; + + case 3: + textLayer.Text = firstPass[0]; + textLayer.Position = this.GetPosition(firstPass[1]); + textLayer.TextColor = this.GetColor(firstPass[2]); + break; + + case 4: + textLayer.Text = firstPass[0]; + textLayer.Position = this.GetPosition(firstPass[1]); + textLayer.TextColor = this.GetColor(firstPass[2]); + textLayer.FontSize = this.GetFontSize(firstPass[3]); + break; + + case 5: + textLayer.Text = firstPass[0]; + textLayer.Position = this.GetPosition(firstPass[1]); + textLayer.TextColor = this.GetColor(firstPass[2]); + textLayer.FontSize = this.GetFontSize(firstPass[3]); + textLayer.Style = this.GetFontStyle(firstPass[4]); + break; + } + + this.DynamicParameter = textLayer; + } + + 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 + { + newImage = new Bitmap(image) { Tag = image.Tag }; + + TextLayer textLayer = this.DynamicParameter; + string text = textLayer.Text; + int opacity = textLayer.Opacity; + int fontSize = textLayer.FontSize; + FontStyle fontStyle = textLayer.Style; + + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (Font font = this.GetFont(textLayer.Font, fontSize, fontStyle)) + { + using (StringFormat drawFormat = new StringFormat()) + { + using (Brush brush = new SolidBrush(Color.FromArgb(opacity, textLayer.TextColor))) + { + Point origin = textLayer.Position; + + // We need to ensure that there is a position set for the watermark + if (origin == Point.Empty) + { + // Work out the size of the text. + SizeF textSize = graphics.MeasureString(text, font, new SizeF(image.Width, image.Height), drawFormat); + + int x = (int)(image.Width - textSize.Width) / 2; + int y = (int)(image.Height - textSize.Height) / 2; + origin = new Point(x, y); + } + + graphics.DrawString(text, font, brush, origin, drawFormat); + } + } + } + + image.Dispose(); + image = newImage; + } + } + catch + { + if (newImage != null) + { + newImage.Dispose(); + } + } + + return image; + } + #endregion + + #region Private Methods + /// + /// Returns the correct for the given parameters + /// + /// + /// The font. + /// + /// + /// The font size. + /// + /// + /// The font style. + /// + /// + /// The correct + /// + private Font GetFont(string font, int fontSize, FontStyle fontStyle) + { + FontFamily fontFamily = string.IsNullOrEmpty(font) ? FontFamily.GenericSansSerif : new FontFamily(font); + + return new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct + /// + private Point GetPosition(string input) + { + int[] position = input.ToIntegerArray(); + int x = position[0]; + int y = position[1]; + + return new Point(x, y); + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct + /// + private Color GetColor(string input) + { + // split on color-hex + return ColorTranslator.FromHtml("#" + input.Split('-')[1]); + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct + /// + private int GetFontSize(string input) + { + // split on size-value + return int.Parse(input.Split('-')[1]); + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The string containing the respective fontstyle. + /// + /// + /// The correct + /// + private FontStyle GetFontStyle(string input) + { + FontStyle fontStyle = FontStyle.Bold; + + switch (input.ToLowerInvariant()) + { + case "italic": + fontStyle = FontStyle.Italic; + break; + case "regular": + fontStyle = FontStyle.Regular; + break; + case "strikeout": + fontStyle = FontStyle.Strikeout; + break; + case "underline": + fontStyle = FontStyle.Underline; + break; + } + + return fontStyle; + } + #endregion + } +} diff --git a/src/ImageProcessor/Processors/Crop.cs b/src/ImageProcessor/Processors/Crop.cs index fb5810989..6e9dd1359 100644 --- a/src/ImageProcessor/Processors/Crop.cs +++ b/src/ImageProcessor/Processors/Crop.cs @@ -162,7 +162,9 @@ namespace ImageProcessor.Processors rectangle.Height = sourceHeight - rectangle.Y; } - newImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb) { Tag = image.Tag }; + // Dont use an object initializer here. + newImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); + newImage.Tag = image.Tag; using (Graphics graphics = Graphics.FromImage(newImage)) { diff --git a/src/ImageProcessor/Processors/Filter.cs b/src/ImageProcessor/Processors/Filter.cs index 88cf22d1a..0dec514bf 100644 --- a/src/ImageProcessor/Processors/Filter.cs +++ b/src/ImageProcessor/Processors/Filter.cs @@ -35,9 +35,24 @@ namespace ImageProcessor.Processors /// private enum ChannelArgb { + /// + /// The blue channel + /// Blue = 0, + + /// + /// The green channel + /// Green = 1, + + /// + /// The red channel + /// Red = 2, + + /// + /// The alpha channel + /// Alpha = 3 } @@ -150,9 +165,15 @@ namespace ImageProcessor.Processors { Bitmap newImage = null; Image image = factory.Image; + // Bitmaps for comic pattern + Bitmap hisatchBitmap = null; + Bitmap patternBitmap = null; + try { - newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb) { Tag = image.Tag }; + // Dont use an object initializer here. + newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); + newImage.Tag = image.Tag; ColorMatrix colorMatrix = null; @@ -207,7 +228,7 @@ namespace ImageProcessor.Processors graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); // Create a bitmap for overlaying. - Bitmap hisatchBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); + hisatchBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); // Set the color matrix attributes.SetColorMatrix(ColorMatrixes.HiSatch); @@ -220,7 +241,7 @@ namespace ImageProcessor.Processors // We need to create a new image now with the hi saturation colormatrix and a pattern mask to paint it // onto the other image with. - Bitmap patternBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); + patternBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); // Create the pattern mask. using (var g = Graphics.FromImage(patternBitmap)) @@ -318,6 +339,16 @@ namespace ImageProcessor.Processors { newImage.Dispose(); } + + if (hisatchBitmap != null) + { + hisatchBitmap.Dispose(); + } + + if (patternBitmap != null) + { + patternBitmap.Dispose(); + } } return image; diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs index 8fe518796..7f3196c81 100644 --- a/src/ImageProcessor/Processors/Resize.cs +++ b/src/ImageProcessor/Processors/Resize.cs @@ -176,7 +176,9 @@ namespace ImageProcessor.Processors if (width > 0 && height > 0 && width <= maxWidth && height <= maxHeight) { - newImage = new Bitmap(width, height, PixelFormat.Format32bppPArgb) { Tag = image.Tag }; + // Dont use an object initializer here. + newImage = new Bitmap(width, height, PixelFormat.Format32bppPArgb); + newImage.Tag = image.Tag; using (Graphics graphics = Graphics.FromImage(newImage)) { diff --git a/src/ImageProcessor/Processors/Vignette.cs b/src/ImageProcessor/Processors/Vignette.cs index fa1f5d880..0fc7f7fe3 100644 --- a/src/ImageProcessor/Processors/Vignette.cs +++ b/src/ImageProcessor/Processors/Vignette.cs @@ -137,7 +137,9 @@ namespace ImageProcessor.Processors try { - newImage = new Bitmap(image) { Tag = image.Tag }; + // Dont use an object initializer here. + newImage = new Bitmap(image); + newImage.Tag = image.Tag; using (Graphics graphics = Graphics.FromImage(newImage)) { diff --git a/src/ImageProcessor/Processors/Watermark.cs b/src/ImageProcessor/Processors/Watermark.cs new file mode 100644 index 000000000..48a56cc05 --- /dev/null +++ b/src/ImageProcessor/Processors/Watermark.cs @@ -0,0 +1,444 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Processors +{ + #region Using + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Text; + using System.Text.RegularExpressions; + using ImageProcessor.Helpers.Extensions; + using ImageProcessor.Imaging; + #endregion + + /// + /// Encapsulates methods to change the alpha component of the image to effect its transparency. + /// + public class Watermark : IGraphicsProcessor + { + /// + /// The regular expression to search strings for. + /// + private static readonly Regex QueryRegex = new Regex(@"watermark=[^&]*", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the text attribute. + /// + private static readonly Regex TextRegex = new Regex(@"text-[^/:?#\[\]@!$&'()*%\|,;=]+", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the position attribute. + /// + private static readonly Regex PositionRegex = new Regex(@"position-\d+-\d+", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the color attribute. + /// + private static readonly Regex ColorRegex = new Regex(@"color-([0-9a-fA-F]{3}){1,2}", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the fontsize attribute. + /// + private static readonly Regex FontSizeRegex = new Regex(@"size-\d{1,3}", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the fontstyle attribute. + /// + private static readonly Regex FontStyleRegex = new Regex(@"style-(bold|italic|regular|strikeout|underline)", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the font family attribute. + /// + private static readonly Regex FontFamilyRegex = new Regex(@"font-[^/:?#\[\]@!$&'()*%\|,;=0-9]+", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the opacity attribute. + /// + private static readonly Regex OpacityRegex = new Regex(@"opacity-\d{1,2}(?!\d)|opacity-100", RegexOptions.Compiled); + + #region IGraphicsProcessor Members + /// + /// Gets the name. + /// + public string Name + { + get + { + return "Watermark"; + } + } + + /// + /// Gets the description. + /// + public string Description + { + get + { + return "Adds a watermark containing text to 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; + + TextLayer textLayer = new TextLayer(); + + string toParse = match.Value; + + textLayer.Text = this.ParseText(toParse); + textLayer.Position = this.ParsePosition(toParse); + textLayer.TextColor = this.ParseColor(toParse); + textLayer.FontSize = this.ParseFontSize(toParse); + textLayer.Font = this.ParseFontFamily(toParse); + textLayer.Style = this.ParseFontStyle(toParse); + textLayer.Opacity = this.ParseOpacity(toParse); + + this.DynamicParameter = textLayer; + } + + 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 + { + // Dont use an object initializer here. + newImage = new Bitmap(image); + newImage.Tag = image.Tag; + + TextLayer textLayer = this.DynamicParameter; + string text = textLayer.Text; + int opacity = textLayer.Opacity; + int fontSize = textLayer.FontSize; + FontStyle fontStyle = textLayer.Style; + + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (Font font = this.GetFont(textLayer.Font, fontSize, fontStyle)) + { + using (StringFormat drawFormat = new StringFormat()) + { + using (Brush brush = new SolidBrush(Color.FromArgb(opacity, textLayer.TextColor))) + { + Point origin = textLayer.Position; + + // We need to ensure that there is a position set for the watermark + if (origin == Point.Empty) + { + // Work out the size of the text. + SizeF textSize = graphics.MeasureString(text, font, new SizeF(image.Width, image.Height), drawFormat); + + int x = (int)(image.Width - textSize.Width) / 2; + int y = (int)(image.Height - textSize.Height) / 2; + origin = new Point(x, y); + } + + // Set the hinting and draw the text. + graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + graphics.DrawString(text, font, brush, origin, drawFormat); + } + } + } + + image.Dispose(); + image = newImage; + } + } + catch + { + if (newImage != null) + { + newImage.Dispose(); + } + } + + return image; + } + #endregion + + #region Private Methods + /// + /// Returns the correct for the given parameters + /// + /// + /// The font. + /// + /// + /// The font size. + /// + /// + /// The font style. + /// + /// + /// The correct + /// + private Font GetFont(string font, int fontSize, FontStyle fontStyle) + { + try + { + using (FontFamily fontFamily = new FontFamily(font)) + { + return new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); + } + } + catch + { + using (FontFamily fontFamily = FontFamily.GenericSansSerif) + { + return new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); + } + } + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct for the given string. + /// + private string ParseText(string input) + { + foreach (Match match in TextRegex.Matches(input)) + { + // split on text- + return match.Value.Split('-')[1].Replace("+", " "); + } + + return string.Empty; + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct + /// + private Point ParsePosition(string input) + { + foreach (Match match in PositionRegex.Matches(input)) + { + int[] position = match.Value.ToIntegerArray(); + + if (position != null) + { + int x = position[0]; + int y = position[1]; + + return new Point(x, y); + } + } + + return Point.Empty; + } + + /// + /// 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.Black; + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct + /// + private int ParseFontSize(string input) + { + foreach (Match match in FontSizeRegex.Matches(input)) + { + // split on size-value + return int.Parse(match.Value.Split('-')[1]); + } + + // Matches the default number in TextLayer. + return 48; + } + + /// + /// Returns the correct for the given string. + /// + /// + /// The string containing the respective fontstyle. + /// + /// + /// The correct + /// + private FontStyle ParseFontStyle(string input) + { + FontStyle fontStyle = FontStyle.Bold; + + foreach (Match match in FontStyleRegex.Matches(input)) + { + // split on style- + switch (match.Value.Split('-')[1]) + { + case "italic": + fontStyle = FontStyle.Italic; + break; + case "regular": + fontStyle = FontStyle.Regular; + break; + case "strikeout": + fontStyle = FontStyle.Strikeout; + break; + case "underline": + fontStyle = FontStyle.Underline; + break; + } + } + + return fontStyle; + } + + /// + /// Returns the correct containing the font family for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct containing the font family for the given string. + /// + private string ParseFontFamily(string input) + { + foreach (Match match in FontFamilyRegex.Matches(input)) + { + // split on font- + string font = match.Value.Split('-')[1].Replace("+", " "); + + return font; + } + + return string.Empty; + } + + /// + /// Returns the correct containing the opacity for the given string. + /// + /// + /// The input string containing the value to parse. + /// + /// + /// The correct containing the font family for the given string. + /// + private int ParseOpacity(string input) + { + foreach (Match match in OpacityRegex.Matches(input)) + { + // split on opacity- + return int.Parse(match.Value.Split('-')[1]); + } + + // full opacity - matches the Textlayer default. + return 100; + } + #endregion + } +} diff --git a/src/ImageProcessor/Properties/AssemblyInfo.cs b/src/ImageProcessor/Properties/AssemblyInfo.cs index 20aa364de..2827a2407 100644 --- a/src/ImageProcessor/Properties/AssemblyInfo.cs +++ b/src/ImageProcessor/Properties/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -34,3 +35,4 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +