From bc5b8df673d2e8badf38fa6a51dd90a788301f35 Mon Sep 17 00:00:00 2001 From: James South Date: Fri, 17 Aug 2012 17:20:39 +0100 Subject: [PATCH] Added rotate method. Former-commit-id: 2c3f1b222b25ebe8f54973e4507fa3e9118ae839 --- src/ImageProcessor/ImageFactory.cs | 19 + src/ImageProcessor/ImageProcessor.csproj | 13 + .../Imaging/Filters/BlackWhiteMatrixFilter.cs | 64 ++ .../Imaging/Filters/ColorMatrixes.cs | 193 ++++++ .../Imaging/Filters/ComicMatrixFilter.cs | 222 +++++++ .../Imaging/Filters/GothamMatrixFilter.cs | 85 +++ .../Imaging/Filters/GreyScaleMatrixFilter.cs | 64 ++ .../Imaging/Filters/HiSatchMatrixFilter.cs | 64 ++ .../Imaging/Filters/IMatrixFilter.cs | 40 ++ .../Imaging/Filters/InvertMatrixFilter.cs | 64 ++ .../Imaging/Filters/LoSatchMatrixFilter.cs | 64 ++ .../Imaging/Filters/LomographMatrixFilter.cs | 70 ++ .../Imaging/Filters/PolaroidMatrixFilter.cs | 99 +++ .../Imaging/Filters/SepiaMatrixFilter.cs | 64 ++ src/ImageProcessor/Imaging/TextLayer.cs | 2 +- .../Processors/Copy of Filter.cs | 610 ++++++++++++++++++ src/ImageProcessor/Processors/Filter.cs | 435 +------------ src/ImageProcessor/Processors/Resize.cs | 24 +- src/ImageProcessor/Processors/Rotate.cs | 282 ++++++++ 19 files changed, 2054 insertions(+), 424 deletions(-) create mode 100644 src/ImageProcessor/Imaging/Filters/BlackWhiteMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/ColorMatrixes.cs create mode 100644 src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/GothamMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/GreyScaleMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/HiSatchMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/IMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/InvertMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/LoSatchMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/PolaroidMatrixFilter.cs create mode 100644 src/ImageProcessor/Imaging/Filters/SepiaMatrixFilter.cs create mode 100644 src/ImageProcessor/Processors/Copy of Filter.cs create mode 100644 src/ImageProcessor/Processors/Rotate.cs diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs index 7eded8bd4..69b0a7556 100644 --- a/src/ImageProcessor/ImageFactory.cs +++ b/src/ImageProcessor/ImageFactory.cs @@ -309,6 +309,25 @@ namespace ImageProcessor return this; } + /// + /// Rotates the current image by the given angle. + /// + /// The angle by which to rotate the image. + /// + /// The current instance of the class. + /// + public ImageFactory Rotate(int angle) + { + if (this.ShouldProcess) + { + Rotate rotate = new Rotate { DynamicParameter = angle }; + + this.Image = rotate.ProcessImage(this); + } + + return this; + } + /// /// Adds a vignette image effect to the current image. /// diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 8bab589e6..5eee94bd0 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -60,12 +60,25 @@ + + + + + + + + + + + + + diff --git a/src/ImageProcessor/Imaging/Filters/BlackWhiteMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/BlackWhiteMatrixFilter.cs new file mode 100644 index 000000000..1856422b3 --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/BlackWhiteMatrixFilter.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + #endregion + + /// + /// Encapsulates methods with which to add a black and white filter to an image. + /// + class BlackWhiteMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.BlackWhite; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/ColorMatrixes.cs b/src/ImageProcessor/Imaging/Filters/ColorMatrixes.cs new file mode 100644 index 000000000..363bae499 --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/ColorMatrixes.cs @@ -0,0 +1,193 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Filters +{ + #region Using + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Drawing.Imaging; + #endregion + + /// + /// A list of available color matrices to apply to an image. + /// + internal static class ColorMatrixes + { + /// + /// Gets Sepia. + /// + internal static ColorMatrix Sepia + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { .393f, .349f, .272f, 0, 0 }, + new float[] { .769f, .686f, .534f, 0, 0 }, + new float[] { .189f, .168f, .131f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }); + } + } + + /// + /// Gets BlackWhite. + /// + internal static ColorMatrix BlackWhite + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, + new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, + new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { -1, -1, -1, 0, 1 } + }); + } + } + + /// + /// Gets Polaroid. + /// + internal static ColorMatrix Polaroid + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1.638f, -0.062f, -0.262f, 0, 0 }, + new float[] { -0.122f, 1.378f, -0.122f, 0, 0 }, + new float[] { 1.016f, -0.016f, 1.383f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0.06f, -0.05f, -0.05f, 0, 1 } + }); + } + } + + /// + /// Gets Lomograph. + /// + internal static ColorMatrix Lomograph + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1.50f, 0, 0, 0, 0 }, + new float[] { 0, 1.45f, 0, 0, 0 }, + new float[] { 0, 0, 1.09f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { -0.10f, 0.05f, -0.08f, 0, 1 } + }); + } + } + + /// + /// Gets GreyScale. + /// + internal static ColorMatrix GreyScale + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { .33f, .33f, .33f, 0, 0 }, + new float[] { .59f, .59f, .59f, 0, 0 }, + new float[] { .11f, .11f, .11f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }); + } + } + + /// + /// Gets Gotham. + /// + internal static ColorMatrix Gotham + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { .9f, .9f, .9f, 0, 0 }, + new float[] { .9f, .9f, .9f, 0, 0 }, + new float[] { .9f, .9f, .9f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { -.5f, -.5f, -.45f, 0, 1 } + }); + } + } + + /// + /// Gets Invert. + /// + internal static ColorMatrix Invert + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { -1, 0, 0, 0, 0 }, + new float[] { 0, -1, 0, 0, 0 }, + new float[] { 0, 0, -1, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 1, 1, 1, 0, 1 } + }); + } + } + + /// + /// Gets HiSatch. + /// + internal static ColorMatrix HiSatch + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 3, -1, -1, 0, 0 }, + new float[] { -1, 3, -1, 0, 0 }, + new float[] { -1, -1, 3, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }); + } + } + + /// + /// Gets LoSatch. + /// + internal static ColorMatrix LoSatch + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1, 0, 0, 0, 0 }, + new float[] { 0, 1, 0, 0, 0 }, + new float[] { 0, 0, 1, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { .25f, .25f, .25f, 0, 1 } + }); + } + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs new file mode 100644 index 000000000..075e3748d --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs @@ -0,0 +1,222 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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 System.Drawing.Drawing2D; + #endregion + + /// + /// Encapsulates methods with which to add a comic filter to an image. + /// + class ComicMatrixFilter : IMatrixFilter + { + /// + /// Enumurates Argb colour channels. + /// + private enum ChannelArgb + { + /// + /// The blue channel + /// + Blue = 0, + + /// + /// The green channel + /// + Green = 1, + + /// + /// The red channel + /// + Red = 2, + + /// + /// The alpha channel + /// + Alpha = 3 + } + + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.LoSatch; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + // Bitmaps for comic pattern + Bitmap hisatchBitmap = null; + Bitmap patternBitmap = null; + + try + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + // Set the attributes to LoSatch and draw the image. + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + + // Create a bitmap for overlaying. + hisatchBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); + + // Set the color matrix + attributes.SetColorMatrix(ColorMatrixes.HiSatch); + + // Draw the image with the hisatch colormatrix. + using (var g = Graphics.FromImage(hisatchBitmap)) + { + g.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + + // 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. + patternBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); + + // Create the pattern mask. + using (var g = Graphics.FromImage(patternBitmap)) + { + g.Clear(Color.Black); + g.SmoothingMode = SmoothingMode.HighQuality; + for (var y = 0; y < image.Height; y += 10) + { + for (var x = 0; x < image.Width; x += 6) + { + g.FillEllipse(Brushes.White, x, y, 4, 4); + g.FillEllipse(Brushes.White, x + 3, y + 5, 4, 4); + } + } + } + + // Transfer the alpha channel from the mask to the hi sturation image. + TransferOneArgbChannelFromOneBitmapToAnother(patternBitmap, hisatchBitmap, ChannelArgb.Blue, ChannelArgb.Alpha); + + // Overlay the image. + graphics.DrawImage(hisatchBitmap, 0, 0); + + // Dispose of the other images + hisatchBitmap.Dispose(); + patternBitmap.Dispose(); + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + } + catch + { + if (newImage != null) + { + newImage.Dispose(); + } + + if (hisatchBitmap != null) + { + hisatchBitmap.Dispose(); + } + + if (patternBitmap != null) + { + patternBitmap.Dispose(); + } + } + return image; + } + + /// + /// Transfers a single ARGB channel from one image to another. + /// + /// + /// The source. + /// + /// + /// The destination. + /// + /// + /// The source channel. + /// + /// + /// The destination channel. + /// + private static void TransferOneArgbChannelFromOneBitmapToAnother(Bitmap source, Bitmap destination, ChannelArgb sourceChannel, ChannelArgb destinationChannel) + { + if (source.Size != destination.Size) + { + throw new ArgumentException(); + } + + Rectangle rectangle = new Rectangle(Point.Empty, source.Size); + + // Lockbits the source. + BitmapData bitmapDataSource = source.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + + // Declare an array to hold the bytes of the bitmap. + int bytes = bitmapDataSource.Stride * bitmapDataSource.Height; + + // Allocate a buffer for the source image + byte[] sourceRgbValues = new byte[bytes]; + + // Copy the RGB values into the array. + System.Runtime.InteropServices.Marshal.Copy(bitmapDataSource.Scan0, sourceRgbValues, 0, bytes); + + // Unlockbits the source. + source.UnlockBits(bitmapDataSource); + + // Lockbits the destination. + BitmapData bitmapDataDestination = destination.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + + // Allocate a buffer for image + byte[] destinationRgbValues = new byte[bytes]; + + // Copy the RGB values into the array. + System.Runtime.InteropServices.Marshal.Copy(bitmapDataDestination.Scan0, destinationRgbValues, 0, bytes); + + int s = (int)sourceChannel; + int d = (int)destinationChannel; + + for (int i = rectangle.Height * rectangle.Width; i > 0; i--) + { + destinationRgbValues[d] = sourceRgbValues[s]; + d += 4; + s += 4; + } + + // Copy the RGB values back to the bitmap + System.Runtime.InteropServices.Marshal.Copy(destinationRgbValues, 0, bitmapDataDestination.Scan0, bytes); + + // Unlock bits the destination. + destination.UnlockBits(bitmapDataDestination); + } + + } +} diff --git a/src/ImageProcessor/Imaging/Filters/GothamMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/GothamMatrixFilter.cs new file mode 100644 index 000000000..d41bad333 --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/GothamMatrixFilter.cs @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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 System.Drawing.Drawing2D; + #endregion + + /// + /// Encapsulates methods with which to add a gotham filter to an image. + /// + class GothamMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.Gotham; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + + // Overlay the image with some semi-transparent colors to finish the effect. + using (GraphicsPath path = new GraphicsPath()) + { + path.AddRectangle(rectangle); + + // Paint a burgundy rectangle with a transparency of ~30% over the image. + // Paint a blue rectangle with a transparency of 20% over the image. + using (SolidBrush brush = new SolidBrush(Color.FromArgb(77, 43, 4, 18))) + { + Region oldClip = graphics.Clip; + graphics.Clip = new Region(rectangle); + graphics.FillRectangle(brush, rectangle); + + // Fill the blue. + brush.Color = Color.FromArgb(51, 12, 22, 88); + graphics.FillRectangle(brush, rectangle); + graphics.Clip = oldClip; + } + } + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/GreyScaleMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/GreyScaleMatrixFilter.cs new file mode 100644 index 000000000..ade0cfcca --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/GreyScaleMatrixFilter.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + #endregion + + /// + /// Encapsulates methods with which to add a greyscale filter to an image. + /// + class GreyScaleMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.GreyScale; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/HiSatchMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/HiSatchMatrixFilter.cs new file mode 100644 index 000000000..33cbddcdb --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/HiSatchMatrixFilter.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + #endregion + + /// + /// Encapsulates methods with which to add a high saturated filter to an image. + /// + class HiSatchMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.HiSatch; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/IMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/IMatrixFilter.cs new file mode 100644 index 000000000..39581ba2d --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/IMatrixFilter.cs @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Filters +{ + #region Using + using System.Drawing; + using System.Drawing.Imaging; + #endregion + + /// + /// Defines properties and methods for ColorMatrix based filters. + /// + interface IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + ColorMatrix Matrix { get; } + + #region Methods + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + Image ProcessImage(ImageFactory factory, Image image, Image newImage); + #endregion + } +} diff --git a/src/ImageProcessor/Imaging/Filters/InvertMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/InvertMatrixFilter.cs new file mode 100644 index 000000000..0d009f177 --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/InvertMatrixFilter.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + #endregion + + /// + /// Encapsulates methods with which to add an inverted filter to an image. + /// + class InvertMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.Invert; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/LoSatchMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/LoSatchMatrixFilter.cs new file mode 100644 index 000000000..91d8fb66b --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/LoSatchMatrixFilter.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + #endregion + + /// + /// Encapsulates methods with which to add a low saturated filter to an image. + /// + class LoSatchMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.LoSatch; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs new file mode 100644 index 000000000..f193e03c9 --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs @@ -0,0 +1,70 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + #endregion + + /// + /// Encapsulates methods with which to add a lomograph filter to an image. + /// + class LomographMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.Lomograph; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + } + + // Add a vignette to finish the effect. + factory.Image = newImage; + Vignette vignette = new Vignette(); + newImage = (Bitmap)vignette.ProcessImage(factory); + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/PolaroidMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/PolaroidMatrixFilter.cs new file mode 100644 index 000000000..d81de098c --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/PolaroidMatrixFilter.cs @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + using System.Drawing.Drawing2D; + #endregion + + /// + /// Encapsulates methods with which to add a polaroid filter to an image. + /// + class PolaroidMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.Polaroid; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + + // Add a glow to the image. + using (GraphicsPath path = new GraphicsPath()) + { + path.AddEllipse(rectangle); + using (PathGradientBrush brush = new PathGradientBrush(path)) + { + // Fill a rectangle with an elliptical gradient brush that goes from orange to transparent. + // This has the effect of painting the far corners transparent and fading in to orange on the + // way in to the centre. + brush.WrapMode = WrapMode.Tile; + brush.CenterColor = Color.FromArgb(70, 255, 153, 102); + brush.SurroundColors = new Color[] { Color.FromArgb(0, 0, 0, 0) }; + + Blend blend = new Blend + { + Positions = new float[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0F }, + Factors = new float[] { 0.0f, 0.5f, 1f, 1f, 1.0f, 1.0f } + }; + + brush.Blend = blend; + + Region oldClip = graphics.Clip; + graphics.Clip = new Region(rectangle); + graphics.FillRectangle(brush, rectangle); + graphics.Clip = oldClip; + } + } + } + } + + // Add a vignette to finish the effect. + factory.Image = newImage; + Vignette vignette = new Vignette(); + newImage = (Bitmap)vignette.ProcessImage(factory); + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/Filters/SepiaMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/SepiaMatrixFilter.cs new file mode 100644 index 000000000..24051140e --- /dev/null +++ b/src/ImageProcessor/Imaging/Filters/SepiaMatrixFilter.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + #endregion + + /// + /// Encapsulates methods with which to add a sepia filter to an image. + /// + class SepiaMatrixFilter : IMatrixFilter + { + /// + /// Gets the for this filter instance. + /// + public ColorMatrix Matrix + { + get { return ColorMatrixes.Sepia; } + } + + /// + /// Processes the image. + /// + /// + /// The the current instance of the class containing + /// the image to process. + /// + /// The current image to process + /// The new Image to return + /// + /// The processed image from the current instance of the class. + /// + public Image ProcessImage(ImageFactory factory, Image image, Image newImage) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(this.Matrix); + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + } + + // Reassign the image. + image.Dispose(); + image = newImage; + + return image; + } + } +} diff --git a/src/ImageProcessor/Imaging/TextLayer.cs b/src/ImageProcessor/Imaging/TextLayer.cs index 9d2bc9f61..6b24c6c32 100644 --- a/src/ImageProcessor/Imaging/TextLayer.cs +++ b/src/ImageProcessor/Imaging/TextLayer.cs @@ -13,7 +13,7 @@ namespace ImageProcessor.Imaging #endregion /// - /// Enacapsulates the properties required to add a leyer of text to an image. + /// Enacapsulates the properties required to add a layer of text to an image. /// public class TextLayer { diff --git a/src/ImageProcessor/Processors/Copy of Filter.cs b/src/ImageProcessor/Processors/Copy of Filter.cs new file mode 100644 index 000000000..df31ffce8 --- /dev/null +++ b/src/ImageProcessor/Processors/Copy of Filter.cs @@ -0,0 +1,610 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Processors +{ + #region Using + + using System; + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.IO; + using System.Text.RegularExpressions; + using System.Web; + using System.Web.Hosting; + + #endregion + + /// + /// Encapsulates methods with which to add filters to an image. + /// + public class Filter : IGraphicsProcessor + { + /// + /// 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); + + /// + /// Enumurates Argb colour channels. + /// + private enum ChannelArgb + { + /// + /// The blue channel + /// + Blue = 0, + + /// + /// The green channel + /// + Green = 1, + + /// + /// The red channel + /// + Red = 2, + + /// + /// The alpha channel + /// + Alpha = 3 + } + + #region IGraphicsProcessor Members + /// + /// Gets the name. + /// + public string Name + { + get + { + return "Filter"; + } + } + + /// + /// Gets the description. + /// + public string Description + { + get + { + return "Encapsulates methods with which to add filters to an image. e.g polaroid, lomograph"; + } + } + + /// + /// 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; + this.DynamicParameter = match.Value.Split('=')[1]; + } + + 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; + + // Bitmaps for comic pattern + Bitmap hisatchBitmap = null; + Bitmap patternBitmap = null; + + try + { + // Dont use an object initializer here. + newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); + newImage.Tag = image.Tag; + + ColorMatrix colorMatrix = null; + + switch ((string)this.DynamicParameter) + { + case "polaroid": + colorMatrix = ColorMatrixes.Polaroid; + break; + case "lomograph": + colorMatrix = ColorMatrixes.Lomograph; + break; + case "sepia": + colorMatrix = ColorMatrixes.Sepia; + break; + case "blackwhite": + colorMatrix = ColorMatrixes.BlackWhite; + break; + case "greyscale": + colorMatrix = ColorMatrixes.GreyScale; + break; + case "gotham": + colorMatrix = ColorMatrixes.Gotham; + break; + case "invert": + colorMatrix = ColorMatrixes.Invert; + break; + case "hisatch": + colorMatrix = ColorMatrixes.HiSatch; + break; + case "losatch": + colorMatrix = ColorMatrixes.LoSatch; + break; + case "comic": + colorMatrix = ColorMatrixes.LoSatch; + break; + } + + using (Graphics graphics = Graphics.FromImage(newImage)) + { + using (ImageAttributes attributes = new ImageAttributes()) + { + if (colorMatrix != null) + { + attributes.SetColorMatrix(colorMatrix); + } + + Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); + + if (this.DynamicParameter == "comic") + { + // Set the attributes to LoSatch and draw the image. + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + + // Create a bitmap for overlaying. + hisatchBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); + + // Set the color matrix + attributes.SetColorMatrix(ColorMatrixes.HiSatch); + + // Draw the image with the hisatch colormatrix. + using (var g = Graphics.FromImage(hisatchBitmap)) + { + g.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + } + + // 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. + patternBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); + + // Create the pattern mask. + using (var g = Graphics.FromImage(patternBitmap)) + { + g.Clear(Color.Black); + g.SmoothingMode = SmoothingMode.HighQuality; + for (var y = 0; y < image.Height; y += 10) + { + for (var x = 0; x < image.Width; x += 6) + { + g.FillEllipse(Brushes.White, x, y, 4, 4); + g.FillEllipse(Brushes.White, x + 3, y + 5, 4, 4); + } + } + } + + // Transfer the alpha channel from the mask to the hi sturation image. + TransferOneArgbChannelFromOneBitmapToAnother(patternBitmap, hisatchBitmap, ChannelArgb.Blue, ChannelArgb.Alpha); + + // Overlay the image. + graphics.DrawImage(hisatchBitmap, 0, 0); + + // Dispose of the other images + hisatchBitmap.Dispose(); + patternBitmap.Dispose(); + } + else + { + graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); + + // Polaroid requires an extra tweak. + if (this.DynamicParameter == "polaroid") + { + using (GraphicsPath path = new GraphicsPath()) + { + path.AddEllipse(rectangle); + using (PathGradientBrush brush = new PathGradientBrush(path)) + { + // Fill a rectangle with an elliptical gradient brush that goes from orange to transparent. + // This has the effect of painting the far corners transparent and fading in to orange on the + // way in to the centre. + brush.WrapMode = WrapMode.Tile; + brush.CenterColor = Color.FromArgb(70, 255, 153, 102); + brush.SurroundColors = new Color[] { Color.FromArgb(0, 0, 0, 0) }; + + Blend blend = new Blend + { + Positions = new float[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0F }, + Factors = new float[] { 0.0f, 0.5f, 1f, 1f, 1.0f, 1.0f } + }; + + brush.Blend = blend; + + Region oldClip = graphics.Clip; + graphics.Clip = new Region(rectangle); + graphics.FillRectangle(brush, rectangle); + graphics.Clip = oldClip; + } + } + } + + // Gotham requires an extra tweak. + if (this.DynamicParameter == "gotham") + { + using (GraphicsPath path = new GraphicsPath()) + { + path.AddRectangle(rectangle); + + // Paint a burgundy rectangle with a transparency of ~30% over the image. + // Paint a blue rectangle with a transparency of 20% over the image. + using (SolidBrush brush = new SolidBrush(Color.FromArgb(77, 43, 4, 18))) + { + Region oldClip = graphics.Clip; + graphics.Clip = new Region(rectangle); + graphics.FillRectangle(brush, rectangle); + + // Fill the blue. + brush.Color = Color.FromArgb(51, 12, 22, 88); + graphics.FillRectangle(brush, rectangle); + graphics.Clip = oldClip; + } + } + } + } + } + } + + // Add a vignette to finish the effect. + // TODO: This feels a bit mucky so I might chop it out. + if (this.DynamicParameter == "polaroid" || this.DynamicParameter == "lomograph") + { + factory.Image = newImage; + Vignette vignette = new Vignette(); + newImage = (Bitmap)vignette.ProcessImage(factory); + } + + // Reassign the image. + image.Dispose(); + image = newImage; + } + catch + { + if (newImage != null) + { + newImage.Dispose(); + } + + if (hisatchBitmap != null) + { + hisatchBitmap.Dispose(); + } + + if (patternBitmap != null) + { + patternBitmap.Dispose(); + } + } + + return image; + } + #endregion + + /// + /// Transfers a single ARGB channel from one image to another. + /// + /// + /// The source. + /// + /// + /// The destination. + /// + /// + /// The source channel. + /// + /// + /// The destination channel. + /// + private static void TransferOneArgbChannelFromOneBitmapToAnother(Bitmap source, Bitmap destination, ChannelArgb sourceChannel, ChannelArgb destinationChannel) + { + if (source.Size != destination.Size) + { + throw new ArgumentException(); + } + + Rectangle rectangle = new Rectangle(Point.Empty, source.Size); + + // Lockbits the source. + BitmapData bitmapDataSource = source.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + + // Declare an array to hold the bytes of the bitmap. + int bytes = bitmapDataSource.Stride * bitmapDataSource.Height; + + // Allocate a buffer for the source image + byte[] sourceRgbValues = new byte[bytes]; + + // Copy the RGB values into the array. + System.Runtime.InteropServices.Marshal.Copy(bitmapDataSource.Scan0, sourceRgbValues, 0, bytes); + + // Unlockbits the source. + source.UnlockBits(bitmapDataSource); + + // Lockbits the destination. + BitmapData bitmapDataDestination = destination.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + + // Allocate a buffer for image + byte[] destinationRgbValues = new byte[bytes]; + + // Copy the RGB values into the array. + System.Runtime.InteropServices.Marshal.Copy(bitmapDataDestination.Scan0, destinationRgbValues, 0, bytes); + + int s = (int)sourceChannel; + int d = (int)destinationChannel; + + for (int i = rectangle.Height * rectangle.Width; i > 0; i--) + { + destinationRgbValues[d] = sourceRgbValues[s]; + d += 4; + s += 4; + } + + // Copy the RGB values back to the bitmap + System.Runtime.InteropServices.Marshal.Copy(destinationRgbValues, 0, bitmapDataDestination.Scan0, bytes); + + // Unlock bits the destination. + destination.UnlockBits(bitmapDataDestination); + } + + /// + /// A list of available color matrices to apply to an image. + /// + private static class ColorMatrixes + { + /// + /// Gets Sepia. + /// + internal static ColorMatrix Sepia + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { .393f, .349f, .272f, 0, 0 }, + new float[] { .769f, .686f, .534f, 0, 0 }, + new float[] { .189f, .168f, .131f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }); + } + } + + /// + /// Gets BlackWhite. + /// + internal static ColorMatrix BlackWhite + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, + new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, + new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { -1, -1, -1, 0, 1 } + }); + } + } + + /// + /// Gets Polaroid. + /// + internal static ColorMatrix Polaroid + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1.638f, -0.062f, -0.262f, 0, 0 }, + new float[] { -0.122f, 1.378f, -0.122f, 0, 0 }, + new float[] { 1.016f, -0.016f, 1.383f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0.06f, -0.05f, -0.05f, 0, 1 } + }); + } + } + + /// + /// Gets Lomograph. + /// + internal static ColorMatrix Lomograph + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1.50f, 0, 0, 0, 0 }, + new float[] { 0, 1.45f, 0, 0, 0 }, + new float[] { 0, 0, 1.09f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { -0.10f, 0.05f, -0.08f, 0, 1 } + }); + } + } + + /// + /// Gets GreyScale. + /// + internal static ColorMatrix GreyScale + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { .33f, .33f, .33f, 0, 0 }, + new float[] { .59f, .59f, .59f, 0, 0 }, + new float[] { .11f, .11f, .11f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }); + } + } + + /// + /// Gets Gotham. + /// + internal static ColorMatrix Gotham + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { .9f, .9f, .9f, 0, 0 }, + new float[] { .9f, .9f, .9f, 0, 0 }, + new float[] { .9f, .9f, .9f, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { -.5f, -.5f, -.45f, 0, 1 } + }); + } + } + + /// + /// Gets Invert. + /// + internal static ColorMatrix Invert + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { -1, 0, 0, 0, 0 }, + new float[] { 0, -1, 0, 0, 0 }, + new float[] { 0, 0, -1, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 1, 1, 1, 0, 1 } + }); + } + } + + /// + /// Gets HiSatch. + /// + internal static ColorMatrix HiSatch + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 3, -1, -1, 0, 0 }, + new float[] { -1, 3, -1, 0, 0 }, + new float[] { -1, -1, 3, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }); + } + } + + /// + /// Gets LoSatch. + /// + internal static ColorMatrix LoSatch + { + get + { + return new ColorMatrix( + new float[][] + { + new float[] { 1, 0, 0, 0, 0 }, + new float[] { 0, 1, 0, 0, 0 }, + new float[] { 0, 0, 1, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { .25f, .25f, .25f, 0, 1 } + }); + } + } + } + } +} diff --git a/src/ImageProcessor/Processors/Filter.cs b/src/ImageProcessor/Processors/Filter.cs index df31ffce8..f1aec4f94 100644 --- a/src/ImageProcessor/Processors/Filter.cs +++ b/src/ImageProcessor/Processors/Filter.cs @@ -17,6 +17,7 @@ namespace ImageProcessor.Processors using System.Text.RegularExpressions; using System.Web; using System.Web.Hosting; + using ImageProcessor.Imaging.Filters; #endregion @@ -30,32 +31,6 @@ namespace ImageProcessor.Processors /// private static readonly Regex QueryRegex = new Regex(@"filter=(lomograph|polaroid|blackwhite|sepia|greyscale|gotham|invert|hisatch|losatch|comic)", RegexOptions.Compiled); - /// - /// Enumurates Argb colour channels. - /// - private enum ChannelArgb - { - /// - /// The blue channel - /// - Blue = 0, - - /// - /// The green channel - /// - Green = 1, - - /// - /// The red channel - /// - Red = 2, - - /// - /// The alpha channel - /// - Alpha = 3 - } - #region IGraphicsProcessor Members /// /// Gets the name. @@ -166,182 +141,50 @@ namespace ImageProcessor.Processors Bitmap newImage = null; Image image = factory.Image; - // Bitmaps for comic pattern - Bitmap hisatchBitmap = null; - Bitmap patternBitmap = null; - try { // Dont use an object initializer here. newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); newImage.Tag = image.Tag; - ColorMatrix colorMatrix = null; + IMatrixFilter matrix = null; switch ((string)this.DynamicParameter) { case "polaroid": - colorMatrix = ColorMatrixes.Polaroid; + matrix = new PolaroidMatrixFilter(); break; case "lomograph": - colorMatrix = ColorMatrixes.Lomograph; + matrix = new LomographMatrixFilter(); break; case "sepia": - colorMatrix = ColorMatrixes.Sepia; + matrix = new SepiaMatrixFilter(); break; case "blackwhite": - colorMatrix = ColorMatrixes.BlackWhite; + matrix = new BlackWhiteMatrixFilter(); break; case "greyscale": - colorMatrix = ColorMatrixes.GreyScale; + matrix = new GreyScaleMatrixFilter(); break; case "gotham": - colorMatrix = ColorMatrixes.Gotham; + matrix = new GothamMatrixFilter(); break; case "invert": - colorMatrix = ColorMatrixes.Invert; + matrix = new InvertMatrixFilter(); break; case "hisatch": - colorMatrix = ColorMatrixes.HiSatch; + matrix = new HiSatchMatrixFilter(); break; case "losatch": - colorMatrix = ColorMatrixes.LoSatch; + matrix = new LoSatchMatrixFilter(); break; case "comic": - colorMatrix = ColorMatrixes.LoSatch; + matrix = new ComicMatrixFilter(); break; } - using (Graphics graphics = Graphics.FromImage(newImage)) - { - using (ImageAttributes attributes = new ImageAttributes()) - { - if (colorMatrix != null) - { - attributes.SetColorMatrix(colorMatrix); - } - - Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height); - - if (this.DynamicParameter == "comic") - { - // Set the attributes to LoSatch and draw the image. - graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); - - // Create a bitmap for overlaying. - hisatchBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); - - // Set the color matrix - attributes.SetColorMatrix(ColorMatrixes.HiSatch); + return matrix.ProcessImage(factory, image, newImage); - // Draw the image with the hisatch colormatrix. - using (var g = Graphics.FromImage(hisatchBitmap)) - { - g.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); - } - - // 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. - patternBitmap = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); - - // Create the pattern mask. - using (var g = Graphics.FromImage(patternBitmap)) - { - g.Clear(Color.Black); - g.SmoothingMode = SmoothingMode.HighQuality; - for (var y = 0; y < image.Height; y += 10) - { - for (var x = 0; x < image.Width; x += 6) - { - g.FillEllipse(Brushes.White, x, y, 4, 4); - g.FillEllipse(Brushes.White, x + 3, y + 5, 4, 4); - } - } - } - - // Transfer the alpha channel from the mask to the hi sturation image. - TransferOneArgbChannelFromOneBitmapToAnother(patternBitmap, hisatchBitmap, ChannelArgb.Blue, ChannelArgb.Alpha); - - // Overlay the image. - graphics.DrawImage(hisatchBitmap, 0, 0); - - // Dispose of the other images - hisatchBitmap.Dispose(); - patternBitmap.Dispose(); - } - else - { - graphics.DrawImage(image, rectangle, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); - - // Polaroid requires an extra tweak. - if (this.DynamicParameter == "polaroid") - { - using (GraphicsPath path = new GraphicsPath()) - { - path.AddEllipse(rectangle); - using (PathGradientBrush brush = new PathGradientBrush(path)) - { - // Fill a rectangle with an elliptical gradient brush that goes from orange to transparent. - // This has the effect of painting the far corners transparent and fading in to orange on the - // way in to the centre. - brush.WrapMode = WrapMode.Tile; - brush.CenterColor = Color.FromArgb(70, 255, 153, 102); - brush.SurroundColors = new Color[] { Color.FromArgb(0, 0, 0, 0) }; - - Blend blend = new Blend - { - Positions = new float[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0F }, - Factors = new float[] { 0.0f, 0.5f, 1f, 1f, 1.0f, 1.0f } - }; - - brush.Blend = blend; - - Region oldClip = graphics.Clip; - graphics.Clip = new Region(rectangle); - graphics.FillRectangle(brush, rectangle); - graphics.Clip = oldClip; - } - } - } - - // Gotham requires an extra tweak. - if (this.DynamicParameter == "gotham") - { - using (GraphicsPath path = new GraphicsPath()) - { - path.AddRectangle(rectangle); - - // Paint a burgundy rectangle with a transparency of ~30% over the image. - // Paint a blue rectangle with a transparency of 20% over the image. - using (SolidBrush brush = new SolidBrush(Color.FromArgb(77, 43, 4, 18))) - { - Region oldClip = graphics.Clip; - graphics.Clip = new Region(rectangle); - graphics.FillRectangle(brush, rectangle); - - // Fill the blue. - brush.Color = Color.FromArgb(51, 12, 22, 88); - graphics.FillRectangle(brush, rectangle); - graphics.Clip = oldClip; - } - } - } - } - } - } - - // Add a vignette to finish the effect. - // TODO: This feels a bit mucky so I might chop it out. - if (this.DynamicParameter == "polaroid" || this.DynamicParameter == "lomograph") - { - factory.Image = newImage; - Vignette vignette = new Vignette(); - newImage = (Bitmap)vignette.ProcessImage(factory); - } - - // Reassign the image. - image.Dispose(); - image = newImage; } catch { @@ -349,262 +192,10 @@ namespace ImageProcessor.Processors { newImage.Dispose(); } - - if (hisatchBitmap != null) - { - hisatchBitmap.Dispose(); - } - - if (patternBitmap != null) - { - patternBitmap.Dispose(); - } } return image; } #endregion - - /// - /// Transfers a single ARGB channel from one image to another. - /// - /// - /// The source. - /// - /// - /// The destination. - /// - /// - /// The source channel. - /// - /// - /// The destination channel. - /// - private static void TransferOneArgbChannelFromOneBitmapToAnother(Bitmap source, Bitmap destination, ChannelArgb sourceChannel, ChannelArgb destinationChannel) - { - if (source.Size != destination.Size) - { - throw new ArgumentException(); - } - - Rectangle rectangle = new Rectangle(Point.Empty, source.Size); - - // Lockbits the source. - BitmapData bitmapDataSource = source.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); - - // Declare an array to hold the bytes of the bitmap. - int bytes = bitmapDataSource.Stride * bitmapDataSource.Height; - - // Allocate a buffer for the source image - byte[] sourceRgbValues = new byte[bytes]; - - // Copy the RGB values into the array. - System.Runtime.InteropServices.Marshal.Copy(bitmapDataSource.Scan0, sourceRgbValues, 0, bytes); - - // Unlockbits the source. - source.UnlockBits(bitmapDataSource); - - // Lockbits the destination. - BitmapData bitmapDataDestination = destination.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); - - // Allocate a buffer for image - byte[] destinationRgbValues = new byte[bytes]; - - // Copy the RGB values into the array. - System.Runtime.InteropServices.Marshal.Copy(bitmapDataDestination.Scan0, destinationRgbValues, 0, bytes); - - int s = (int)sourceChannel; - int d = (int)destinationChannel; - - for (int i = rectangle.Height * rectangle.Width; i > 0; i--) - { - destinationRgbValues[d] = sourceRgbValues[s]; - d += 4; - s += 4; - } - - // Copy the RGB values back to the bitmap - System.Runtime.InteropServices.Marshal.Copy(destinationRgbValues, 0, bitmapDataDestination.Scan0, bytes); - - // Unlock bits the destination. - destination.UnlockBits(bitmapDataDestination); - } - - /// - /// A list of available color matrices to apply to an image. - /// - private static class ColorMatrixes - { - /// - /// Gets Sepia. - /// - internal static ColorMatrix Sepia - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { .393f, .349f, .272f, 0, 0 }, - new float[] { .769f, .686f, .534f, 0, 0 }, - new float[] { .189f, .168f, .131f, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { 0, 0, 0, 0, 1 } - }); - } - } - - /// - /// Gets BlackWhite. - /// - internal static ColorMatrix BlackWhite - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, - new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, - new float[] { 1.5f, 1.5f, 1.5f, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { -1, -1, -1, 0, 1 } - }); - } - } - - /// - /// Gets Polaroid. - /// - internal static ColorMatrix Polaroid - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { 1.638f, -0.062f, -0.262f, 0, 0 }, - new float[] { -0.122f, 1.378f, -0.122f, 0, 0 }, - new float[] { 1.016f, -0.016f, 1.383f, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { 0.06f, -0.05f, -0.05f, 0, 1 } - }); - } - } - - /// - /// Gets Lomograph. - /// - internal static ColorMatrix Lomograph - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { 1.50f, 0, 0, 0, 0 }, - new float[] { 0, 1.45f, 0, 0, 0 }, - new float[] { 0, 0, 1.09f, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { -0.10f, 0.05f, -0.08f, 0, 1 } - }); - } - } - - /// - /// Gets GreyScale. - /// - internal static ColorMatrix GreyScale - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { .33f, .33f, .33f, 0, 0 }, - new float[] { .59f, .59f, .59f, 0, 0 }, - new float[] { .11f, .11f, .11f, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { 0, 0, 0, 0, 1 } - }); - } - } - - /// - /// Gets Gotham. - /// - internal static ColorMatrix Gotham - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { .9f, .9f, .9f, 0, 0 }, - new float[] { .9f, .9f, .9f, 0, 0 }, - new float[] { .9f, .9f, .9f, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { -.5f, -.5f, -.45f, 0, 1 } - }); - } - } - - /// - /// Gets Invert. - /// - internal static ColorMatrix Invert - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { -1, 0, 0, 0, 0 }, - new float[] { 0, -1, 0, 0, 0 }, - new float[] { 0, 0, -1, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { 1, 1, 1, 0, 1 } - }); - } - } - - /// - /// Gets HiSatch. - /// - internal static ColorMatrix HiSatch - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { 3, -1, -1, 0, 0 }, - new float[] { -1, 3, -1, 0, 0 }, - new float[] { -1, -1, 3, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { 0, 0, 0, 0, 1 } - }); - } - } - - /// - /// Gets LoSatch. - /// - internal static ColorMatrix LoSatch - { - get - { - return new ColorMatrix( - new float[][] - { - new float[] { 1, 0, 0, 0, 0 }, - new float[] { 0, 1, 0, 0, 0 }, - new float[] { 0, 0, 1, 0, 0 }, - new float[] { 0, 0, 0, 1, 0 }, - new float[] { .25f, .25f, .25f, 0, 1 } - }); - } - } - } } } diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs index 9f66346be..f99e9a720 100644 --- a/src/ImageProcessor/Processors/Resize.cs +++ b/src/ImageProcessor/Processors/Resize.cs @@ -24,7 +24,7 @@ namespace ImageProcessor.Processors /// /// The regular expression to search strings for. /// - private static readonly Regex QueryRegex = new Regex(@"((width|height)=\d+|resize=width-\d+\|height-\d+)", RegexOptions.Compiled); + private static readonly Regex QueryRegex = new Regex(@"(resize=width-\d+\|height-\d+|width=\d+\&height=\d+|height=\d+\&width=\d+|(width|height)=\d+)", RegexOptions.Compiled); #region IGraphicsProcessor Members /// @@ -114,6 +114,7 @@ namespace ImageProcessor.Processors this.SortOrder = match.Index; } + // Resize syntax if (match.Value.Contains("resize")) { int[] values = match.Value.ToIntegerArray(); @@ -121,8 +122,29 @@ namespace ImageProcessor.Processors size.Width = values[0]; size.Height = values[1]; } + else if (match.Value.Contains("width") && match.Value.Contains("height")) + { + // Combined width/height syntax + int widthPosition = match.Value.IndexOf("width"); + int heightPosition = match.Value.IndexOf("height"); + + int[] values = match.Value.ToIntegerArray(); + + if (widthPosition < heightPosition) + { + size.Width = values[0]; + size.Height = values[1]; + + } + else + { + size.Width = values[1]; + size.Height = values[0]; + } + } else { + // Inividual syntax if (match.Value.Contains("width")) { size.Width = match.Value.ToIntegerArray()[0]; diff --git a/src/ImageProcessor/Processors/Rotate.cs b/src/ImageProcessor/Processors/Rotate.cs new file mode 100644 index 000000000..e8161ef30 --- /dev/null +++ b/src/ImageProcessor/Processors/Rotate.cs @@ -0,0 +1,282 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace ImageProcessor.Processors +{ + #region Using + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Imaging; + using System.Text.RegularExpressions; + using ImageProcessor.Helpers.Extensions; + using System; + using System.Drawing.Drawing2D; + #endregion + + /// + /// Encapsulates methods to rotate an image. + /// + public class Rotate : IGraphicsProcessor + { + /// + /// The regular expression to search strings for. + /// + //private static readonly Regex QueryRegex = new Regex(@"rotate=-*([1-9][0-7][0-9]|\d{1,2}(?!\d)|180)|rotate=[^&]*", RegexOptions.Compiled); + private static readonly Regex QueryRegex = new Regex(@"rotate=-*([1-2][0-9][0-9]|3[0-5][0-9]|\d{1}(?!\d)|\d{1,2}(?!\d)|360)|rotate=[^&]*", RegexOptions.Compiled); + + /// + /// The regular expression to search strings for the angle attribute. + /// + private static readonly Regex AngleRegex = new Regex(@"rotate=\[-*([1-9][0-7][0-9]|\d{1,2}(?!\d)|180)\]", 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); + + /// + /// The format of the image to rotate. + /// + private ImageFormat imageFormat; + + #region IGraphicsProcessor Members + /// + /// Gets the name. + /// + public string Name + { + get + { + return "Rotate"; + } + } + + /// + /// Gets the description. + /// + public string Description + { + get + { + return "Rotates an image at the given angle."; + } + } + + /// + /// 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 degrees; + + int.TryParse(match.Value.Split('=')[1], out degrees); + + this.DynamicParameter = degrees; + } + + 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 + { + int angle = this.DynamicParameter; + + // Center of the image + float rotateAtX = image.Width / 2; + float rotateAtY = image.Height / 2; + + this.imageFormat = factory.ImageFormat; + + // Create a rotated image. + newImage = RotateImage(image, rotateAtX, rotateAtY, angle); + newImage.Tag = image.Tag; + + image.Dispose(); + image = newImage; + + } + catch + { + if (newImage != null) + { + newImage.Dispose(); + } + } + + return image; + } + #endregion + + /// + /// + /// + /// + /// + /// + /// + /// + /// Based on http://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations?msg=4155374#xx4155374xx + private Bitmap RotateImage(Image image, float rotateAtX, float rotateAtY, float angle) + { + int width, height, X, Y; + + // Degrees to radians according to Google. + const double degreeToRadian = 0.0174532925; + + double widthAsDouble = (double)image.Width; + double heightAsDouble = (double)image.Height; + + // Allow for angles over 180 + if (angle > 180) + { + angle = angle - 360; + } + + double degrees = Math.Abs(angle); + + if (degrees <= 90) + { + double radians = degreeToRadian * degrees; + double radiansSin = Math.Sin(radians); + double radiansCos = Math.Cos(radians); + width = (int)(heightAsDouble * radiansSin + widthAsDouble * radiansCos); + height = (int)(widthAsDouble * radiansSin + heightAsDouble * radiansCos); + X = (width - image.Width) / 2; + Y = (height - image.Height) / 2; + } + else + { + degrees -= 90; + double radians = degreeToRadian * degrees; + double radiansSin = Math.Sin(radians); + double radiansCos = Math.Cos(radians); + + // Fix the 270 bug + if (radiansCos == -1) + { + radiansCos = -1 * -1; + } + + width = (int)(widthAsDouble * radiansSin + heightAsDouble * radiansCos); + height = (int)(heightAsDouble * radiansSin + widthAsDouble * radiansCos); + X = (width - image.Width) / 2; + Y = (height - image.Height) / 2; + } + + //create a new empty bitmap to hold rotated image + Bitmap newImage = new Bitmap(width, height); + newImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); + + //make a graphics object from the empty bitmap + using (Graphics graphics = Graphics.FromImage(newImage)) + { + // 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 TODO: Set a color + if (this.imageFormat == ImageFormat.Jpeg) + { + graphics.Clear(Color.White); + } + + // Put the rotation point in the "center" of the image + graphics.TranslateTransform(rotateAtX + X, rotateAtY + Y); + + // Rotate the image + graphics.RotateTransform(angle); + + // Move the image back + graphics.TranslateTransform(-rotateAtX - X, -rotateAtY - Y); + + // Draw passed in image onto graphics object + graphics.DrawImage(image, new PointF(0 + X, 0 + Y)); + + } + return newImage; + } + } +}