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;
+ }
+ }
+}