diff --git a/src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs b/src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs index 8d050226a..1243bd327 100644 --- a/src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs +++ b/src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs @@ -60,7 +60,7 @@ namespace ImageProcessor.Web // Loop through and process the image. foreach (IGraphicsProcessor graphicsProcessor in graphicsProcessors) { - ProcessImage(graphicsProcessor.ProcessImage, factory); + ApplyProcessor(graphicsProcessor.ProcessImage, factory); } } } @@ -77,13 +77,16 @@ namespace ImageProcessor.Web /// /// The factory. /// - private static void ProcessImage(Func processor, ImageFactory 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)) { @@ -96,7 +99,7 @@ namespace ImageProcessor.Web } stream.Position = 0; - factory.Update(new Bitmap(stream)); + factory.Update(Image.FromStream(stream)); } else { diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs index 002093e61..3875ce72c 100644 --- a/src/ImageProcessor/ImageFactory.cs +++ b/src/ImageProcessor/ImageFactory.cs @@ -22,6 +22,7 @@ namespace ImageProcessor using ImageProcessor.Extensions; using ImageProcessor.Imaging; + using ImageProcessor.Imaging.Filters; using ImageProcessor.Processors; #endregion @@ -298,8 +299,7 @@ namespace ImageProcessor } Alpha alpha = new Alpha { DynamicParameter = percentage }; - - this.Image = alpha.ProcessImage(this); + this.ApplyProcessor(alpha.ProcessImage); } return this; @@ -326,8 +326,7 @@ namespace ImageProcessor } Brightness brightness = new Brightness { DynamicParameter = percentage }; - - this.Image = brightness.ProcessImage(this); + this.ApplyProcessor(brightness.ProcessImage); } return this; @@ -375,8 +374,7 @@ namespace ImageProcessor } Contrast contrast = new Contrast { DynamicParameter = percentage }; - - this.Image = contrast.ProcessImage(this); + this.ApplyProcessor(contrast.ProcessImage); } return this; @@ -396,10 +394,7 @@ namespace ImageProcessor if (this.ShouldProcess) { CropLayer cropLayer = new CropLayer(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom, CropMode.Pixels); - - Crop crop = new Crop { DynamicParameter = cropLayer }; - - this.Image = crop.ProcessImage(this); + return this.Crop(cropLayer); } return this; @@ -419,13 +414,12 @@ namespace ImageProcessor if (this.ShouldProcess) { Crop crop = new Crop { DynamicParameter = cropLayer }; - - this.Image = crop.ProcessImage(this); + this.ApplyProcessor(crop.ProcessImage); } return this; } - + /// /// Applies a filter to the current image. /// @@ -435,13 +429,34 @@ namespace ImageProcessor /// /// The current instance of the class. /// + [Obsolete("Will be removed in next major version. Filter(IMatrixFilter matrixFilter) instead.")] public ImageFactory Filter(string filterName) { if (this.ShouldProcess) { Filter filter = new Filter { DynamicParameter = filterName }; + this.ApplyProcessor(filter.ProcessImage); + } + + return this; + } - this.Image = filter.ProcessImage(this); + /// + /// Applies a filter to the current image. Use the class to + /// assign the correct filter. + /// + /// + /// The of the filter to add to the image. + /// + /// + /// The current instance of the class. + /// + public ImageFactory Filter(IMatrixFilter matrixFilter) + { + if (this.ShouldProcess) + { + Filter filter = new Filter { DynamicParameter = matrixFilter }; + this.ApplyProcessor(filter.ProcessImage); } return this; @@ -465,8 +480,7 @@ namespace ImageProcessor : RotateFlipType.RotateNoneFlipY; Flip flip = new Flip { DynamicParameter = rotateFlipType }; - - this.Image = flip.ProcessImage(this); + this.ApplyProcessor(flip.ProcessImage); } return this; @@ -512,8 +526,7 @@ namespace ImageProcessor if (this.ShouldProcess && size > 0) { GaussianLayer layer = new GaussianLayer(size); - GaussianBlur gaussianBlur = new GaussianBlur { DynamicParameter = layer }; - this.Image = gaussianBlur.ProcessImage(this); + return this.GaussianBlur(layer); } return this; @@ -534,7 +547,7 @@ namespace ImageProcessor if (this.ShouldProcess) { GaussianBlur gaussianBlur = new GaussianBlur { DynamicParameter = gaussianLayer }; - this.Image = gaussianBlur.ProcessImage(this); + this.ApplyProcessor(gaussianBlur.ProcessImage); } return this; @@ -560,8 +573,7 @@ namespace ImageProcessor if (this.ShouldProcess && size > 0) { GaussianLayer layer = new GaussianLayer(size); - GaussianSharpen gaussianSharpen = new GaussianSharpen { DynamicParameter = layer }; - this.Image = gaussianSharpen.ProcessImage(this); + return this.GaussianSharpen(layer); } return this; @@ -582,7 +594,7 @@ namespace ImageProcessor if (this.ShouldProcess) { GaussianSharpen gaussianSharpen = new GaussianSharpen { DynamicParameter = gaussianLayer }; - this.Image = gaussianSharpen.ProcessImage(this); + this.ApplyProcessor(gaussianSharpen.ProcessImage); } return this; @@ -625,7 +637,6 @@ namespace ImageProcessor int height = size.Height; ResizeLayer resizeLayer = new ResizeLayer(new Size(width, height)); - return this.Resize(resizeLayer); } @@ -648,8 +659,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.ProcessImage(resize.ProcessImage, resizeLayer.Size.Width, resizeLayer.Size.Height); + this.ApplyProcessor(resize.ProcessImage); } return this; @@ -675,8 +685,7 @@ namespace ImageProcessor } Rotate rotate = new Rotate { DynamicParameter = rotateLayer }; - - this.Image = rotate.ProcessImage(this); + this.ApplyProcessor(rotate.ProcessImage); } return this; @@ -701,8 +710,7 @@ namespace ImageProcessor } RoundedCorners roundedCorners = new RoundedCorners { DynamicParameter = roundedCornerLayer }; - - this.Image = roundedCorners.ProcessImage(this); + this.ApplyProcessor(roundedCorners.ProcessImage); } return this; @@ -729,8 +737,7 @@ namespace ImageProcessor } Saturation saturate = new Saturation { DynamicParameter = percentage }; - - this.Image = saturate.ProcessImage(this); + this.ApplyProcessor(saturate.ProcessImage); } return this; @@ -747,8 +754,7 @@ namespace ImageProcessor if (this.ShouldProcess) { Vignette vignette = new Vignette(); - - this.Image = vignette.ProcessImage(this); + this.ApplyProcessor(vignette.ProcessImage); } return this; @@ -769,8 +775,7 @@ namespace ImageProcessor if (this.ShouldProcess) { Watermark watermark = new Watermark { DynamicParameter = textLayer }; - - this.Image = watermark.ProcessImage(this); + this.ApplyProcessor(watermark.ProcessImage); } return this; @@ -974,26 +979,23 @@ namespace ImageProcessor } /// - /// The process image. + /// Applies the given processor the current image. /// /// - /// The processor. - /// - /// - /// The width. + /// The processor delegate. /// - /// - /// The height. - /// - private void ProcessImage(Func processor, int width, int height) + 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, width, height, imageInfo.LoopCount)) + using (GifEncoder encoder = new GifEncoder(stream, null, null, imageInfo.LoopCount)) { foreach (GifFrame frame in imageInfo.GifFrames) { diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index f1251eb08..45ad36bc2 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -70,6 +70,8 @@ + + diff --git a/src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs index 7d9b4ff1f..c44bce5c8 100644 --- a/src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs +++ b/src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs @@ -11,10 +11,6 @@ namespace ImageProcessor.Imaging.Filters { #region Using - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using System.Drawing; using System.Drawing.Imaging; using ImageProcessor.Processors; diff --git a/src/ImageProcessor/Imaging/Filters/MatrixFilterRegexAttribute.cs b/src/ImageProcessor/Imaging/Filters/MatrixFilterRegexAttribute.cs new file mode 100644 index 000000000..ebd5db379 --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/MatrixFilterRegexAttribute.cs @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// The filter attribute for identifying matrix filter properties. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Filters +{ + using System; + + /// + /// The filter attribute for identifying matrix filter properties. + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class MatrixFilterRegexAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The regex identifier. + /// + public MatrixFilterRegexAttribute(string regexIdentifier) + { + this.RegexIdentifier = regexIdentifier; + } + + /// + /// Gets the regex identifier. + /// + public string RegexIdentifier { get; private set; } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/MatrixFilters.cs b/src/ImageProcessor/Imaging/Filters/MatrixFilters.cs new file mode 100644 index 000000000..8159a4c03 --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/MatrixFilters.cs @@ -0,0 +1,140 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// The filters available to the Filter . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Filters +{ + using ImageProcessor.Processors; + + /// + /// The filters available to the Filter . + /// + public static class MatrixFilters + { + /// + /// Gets the black white filter. + /// + [MatrixFilterRegex("blackwhite")] + public static IMatrixFilter BlackWhite + { + get + { + return new BlackWhiteMatrixFilter(); + } + } + + /// + /// Gets the comic filter. + /// + [MatrixFilterRegex("comic")] + public static IMatrixFilter Comic + { + get + { + return new ComicMatrixFilter(); + } + } + + /// + /// Gets the gotham filter. + /// + [MatrixFilterRegex("gotham")] + public static IMatrixFilter Gotham + { + get + { + return new GothamMatrixFilter(); + } + } + + /// + /// Gets the greyscale filter. + /// + [MatrixFilterRegex("greyscale")] + public static IMatrixFilter GreyScale + { + get + { + return new GreyScaleMatrixFilter(); + } + } + + /// + /// Gets the high saturation filter. + /// + [MatrixFilterRegex("hisatch")] + public static IMatrixFilter HiSatch + { + get + { + return new HiSatchMatrixFilter(); + } + } + + /// + /// Gets the invert filter. + /// + [MatrixFilterRegex("invert")] + public static IMatrixFilter Invert + { + get + { + return new InvertMatrixFilter(); + } + } + + /// + /// Gets the lomograph filter. + /// + [MatrixFilterRegex("lomograph")] + public static IMatrixFilter Lomograph + { + get + { + return new LomographMatrixFilter(); + } + } + + /// + /// Gets the low saturation filter. + /// + [MatrixFilterRegex("losatch")] + public static IMatrixFilter LoSatch + { + get + { + return new LomographMatrixFilter(); + } + } + + /// + /// Gets the polaroid filter. + /// + [MatrixFilterRegex("polaroid")] + public static IMatrixFilter Polaroid + { + get + { + return new PolaroidMatrixFilter(); + } + } + + /// + /// Gets the sepia filter. + /// + [MatrixFilterRegex("sepia")] + public static IMatrixFilter Sepia + { + get + { + return new SepiaMatrixFilter(); + } + } + } +} diff --git a/src/ImageProcessor/Imaging/GifEncoder.cs b/src/ImageProcessor/Imaging/GifEncoder.cs index 09cb1aea9..20a8ba0f0 100644 --- a/src/ImageProcessor/Imaging/GifEncoder.cs +++ b/src/ImageProcessor/Imaging/GifEncoder.cs @@ -9,6 +9,7 @@ // Always wire this up in a using block. // Disposing the encoder will complete the file. // Uses default .NET GIF encoding and adds animation headers. +// Adapted from // // // -------------------------------------------------------------------------------------------------------------------- @@ -17,7 +18,6 @@ namespace ImageProcessor.Imaging { #region Using using System; - using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; @@ -29,6 +29,7 @@ namespace ImageProcessor.Imaging /// Always wire this up in a using block. /// Disposing the encoder will complete the file. /// Uses default .NET GIF encoding and adds animation headers. + /// Adapted from /// /// public class GifEncoder : IDisposable diff --git a/src/ImageProcessor/Processors/Filter.cs b/src/ImageProcessor/Processors/Filter.cs index 8f1b8d5b4..2673c7a29 100644 --- a/src/ImageProcessor/Processors/Filter.cs +++ b/src/ImageProcessor/Processors/Filter.cs @@ -11,9 +11,13 @@ namespace ImageProcessor.Processors { #region Using + using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; + using System.Linq; + using System.Reflection; + using System.Text; using System.Text.RegularExpressions; using ImageProcessor.Imaging.Filters; #endregion @@ -26,7 +30,7 @@ namespace ImageProcessor.Processors /// /// The regular expression to search strings for. /// - private static readonly Regex QueryRegex = new Regex(@"filter=(lomograph|polaroid|blackwhite|sepia|greyscale|gotham|invert|hisatch|losatch|comic)", RegexOptions.Compiled); + private static readonly Regex QueryRegex = BuildRegex(); #region IGraphicsProcessor Members /// @@ -115,45 +119,12 @@ namespace ImageProcessor.Processors { Bitmap newImage = null; Image image = factory.Image; - IMatrixFilter matrix = null; try { newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); - switch ((string)this.DynamicParameter) - { - case "polaroid": - matrix = new PolaroidMatrixFilter(); - break; - case "lomograph": - matrix = new LomographMatrixFilter(); - break; - case "sepia": - matrix = new SepiaMatrixFilter(); - break; - case "blackwhite": - matrix = new BlackWhiteMatrixFilter(); - break; - case "greyscale": - matrix = new GreyScaleMatrixFilter(); - break; - case "gotham": - matrix = new GothamMatrixFilter(); - break; - case "invert": - matrix = new InvertMatrixFilter(); - break; - case "hisatch": - matrix = new HiSatchMatrixFilter(); - break; - case "losatch": - matrix = new LoSatchMatrixFilter(); - break; - case "comic": - matrix = new ComicMatrixFilter(); - break; - } + IMatrixFilter matrix = this.DynamicParameter as IMatrixFilter ?? this.ParseFilter((string)this.DynamicParameter); if (matrix != null) { @@ -171,5 +142,66 @@ namespace ImageProcessor.Processors return image; } #endregion + + /// + /// Builds a regular expression from the type, this allows extensibility. + /// + /// + /// The to match matrix filters. + /// + private static Regex BuildRegex() + { + const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static; + Type type = typeof(MatrixFilters); + IEnumerable filters = type.GetProperties(Flags) + .Where(p => p.IsDefined(typeof(MatrixFilterRegexAttribute), false)) + .ToList(); + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("filter=("); + int counter = 0; + + foreach (PropertyInfo filter in filters) + { + MatrixFilterRegexAttribute attribute = (MatrixFilterRegexAttribute)filter.GetCustomAttributes(typeof(MatrixFilterRegexAttribute), false).First(); + + if (counter == 0) + { + stringBuilder.Append(attribute.RegexIdentifier); + } + else + { + stringBuilder.AppendFormat("|{0}", attribute.RegexIdentifier); + } + + counter++; + } + + stringBuilder.Append(")"); + + return new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled); + } + + /// + /// Parses the filter. + /// + /// + /// The identifier. + /// + /// + /// The . + /// + private IMatrixFilter ParseFilter(string identifier) + { + const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static; + + Type type = typeof(MatrixFilters); + PropertyInfo filter = + type.GetProperties(Flags) + .Where(p => p.IsDefined(typeof(MatrixFilterRegexAttribute), false)) + .First(p => ((MatrixFilterRegexAttribute)p.GetCustomAttributes(typeof(MatrixFilterRegexAttribute), false).First()).RegexIdentifier == identifier); + + return filter.GetValue(null, null) as IMatrixFilter; + } } } diff --git a/src/ImageProcessorConsole/Program.cs b/src/ImageProcessorConsole/Program.cs index 7e71a5b38..7c95ad1ce 100644 --- a/src/ImageProcessorConsole/Program.cs +++ b/src/ImageProcessorConsole/Program.cs @@ -11,6 +11,7 @@ namespace ImageProcessorConsole using System.IO; using ImageProcessor; + using ImageProcessor.Imaging.Filters; class Program { @@ -42,6 +43,7 @@ namespace ImageProcessorConsole // Load, resize, set the format and quality and save an image. imageFactory.Load(inStream) .Constrain(size) + .Filter(MatrixFilters.Comic) .Format(format) .Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name))); } diff --git a/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id b/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id index 71ce555c1..361dec973 100644 --- a/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id +++ b/src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id @@ -1 +1 @@ -30ec5c05548fd350f9b7c699715848b9fbfb8ca9 \ No newline at end of file +7880376ac9108d4e74412efb31f544484079ce16 \ 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 new file mode 100644 index 000000000..3e0788875 --- /dev/null +++ b/src/ImageProcessorConsole/images/output/nLpfllv .gif.REMOVED.git-id @@ -0,0 +1 @@ +673e6c6d9223e0906fe342a17496168b8d9017fb \ 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 deleted file mode 100644 index 4487aede0..000000000 --- a/src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -23a1c81a2d1422076373796e0c47f5d968c56d0b \ No newline at end of file