Browse Source

Added rotate method.

Former-commit-id: 2c3f1b222b25ebe8f54973e4507fa3e9118ae839
af/merge-core
James South 14 years ago
parent
commit
bc5b8df673
  1. 19
      src/ImageProcessor/ImageFactory.cs
  2. 13
      src/ImageProcessor/ImageProcessor.csproj
  3. 64
      src/ImageProcessor/Imaging/Filters/BlackWhiteMatrixFilter.cs
  4. 193
      src/ImageProcessor/Imaging/Filters/ColorMatrixes.cs
  5. 222
      src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs
  6. 85
      src/ImageProcessor/Imaging/Filters/GothamMatrixFilter.cs
  7. 64
      src/ImageProcessor/Imaging/Filters/GreyScaleMatrixFilter.cs
  8. 64
      src/ImageProcessor/Imaging/Filters/HiSatchMatrixFilter.cs
  9. 40
      src/ImageProcessor/Imaging/Filters/IMatrixFilter.cs
  10. 64
      src/ImageProcessor/Imaging/Filters/InvertMatrixFilter.cs
  11. 64
      src/ImageProcessor/Imaging/Filters/LoSatchMatrixFilter.cs
  12. 70
      src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs
  13. 99
      src/ImageProcessor/Imaging/Filters/PolaroidMatrixFilter.cs
  14. 64
      src/ImageProcessor/Imaging/Filters/SepiaMatrixFilter.cs
  15. 2
      src/ImageProcessor/Imaging/TextLayer.cs
  16. 610
      src/ImageProcessor/Processors/Copy of Filter.cs
  17. 435
      src/ImageProcessor/Processors/Filter.cs
  18. 24
      src/ImageProcessor/Processors/Resize.cs
  19. 282
      src/ImageProcessor/Processors/Rotate.cs

19
src/ImageProcessor/ImageFactory.cs

@ -309,6 +309,25 @@ namespace ImageProcessor
return this;
}
/// <summary>
/// Rotates the current image by the given angle.
/// </summary>
/// <param name="angle">The angle by which to rotate the image.</param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Rotate(int angle)
{
if (this.ShouldProcess)
{
Rotate rotate = new Rotate { DynamicParameter = angle };
this.Image = rotate.ProcessImage(this);
}
return this;
}
/// <summary>
/// Adds a vignette image effect to the current image.
/// </summary>

13
src/ImageProcessor/ImageProcessor.csproj

@ -60,12 +60,25 @@
<Compile Include="Helpers\Extensions\EnumExtensions.cs" />
<Compile Include="Helpers\Extensions\StringExtensions.cs" />
<Compile Include="ImageFactory.cs" />
<Compile Include="Imaging\Filters\BlackWhiteMatrixFilter.cs" />
<Compile Include="Imaging\Filters\ColorMatrixes.cs" />
<Compile Include="Imaging\Filters\ComicMatrixFilter.cs" />
<Compile Include="Imaging\Filters\LoSatchMatrixFilter.cs" />
<Compile Include="Imaging\Filters\HiSatchMatrixFilter.cs" />
<Compile Include="Imaging\Filters\InvertMatrixFilter.cs" />
<Compile Include="Imaging\Filters\GothamMatrixFilter.cs" />
<Compile Include="Imaging\Filters\GreyScaleMatrixFilter.cs" />
<Compile Include="Imaging\Filters\LomographMatrixFilter.cs" />
<Compile Include="Imaging\Filters\PolaroidMatrixFilter.cs" />
<Compile Include="Imaging\Filters\SepiaMatrixFilter.cs" />
<Compile Include="Imaging\Filters\IMatrixFilter.cs" />
<Compile Include="Imaging\ImageUtils.cs" />
<Compile Include="Imaging\OctreeQuantizer.cs" />
<Compile Include="Imaging\Quantizer.cs" />
<Compile Include="Imaging\ResponseType.cs" />
<Compile Include="Imaging\TextLayer.cs" />
<Compile Include="Processors\Alpha.cs" />
<Compile Include="Processors\Rotate.cs" />
<Compile Include="Processors\Crop.cs" />
<Compile Include="Processors\Filter.cs" />
<Compile Include="Processors\IGraphicsProcessor.cs" />

64
src/ImageProcessor/Imaging/Filters/BlackWhiteMatrixFilter.cs

@ -0,0 +1,64 @@
// -----------------------------------------------------------------------
// <copyright file="BlackWhiteMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a black and white filter to an image.
/// </summary>
class BlackWhiteMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.BlackWhite; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

193
src/ImageProcessor/Imaging/Filters/ColorMatrixes.cs

@ -0,0 +1,193 @@
// -----------------------------------------------------------------------
// <copyright file="ColorMatrixes.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Imaging.Filters
{
#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Imaging;
#endregion
/// <summary>
/// A list of available color matrices to apply to an image.
/// </summary>
internal static class ColorMatrixes
{
/// <summary>
/// Gets Sepia.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets BlackWhite.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Polaroid.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Lomograph.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets GreyScale.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Gotham.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Invert.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets HiSatch.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets LoSatch.
/// </summary>
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 }
});
}
}
}
}

222
src/ImageProcessor/Imaging/Filters/ComicMatrixFilter.cs

@ -0,0 +1,222 @@
// -----------------------------------------------------------------------
// <copyright file="ComicMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a comic filter to an image.
/// </summary>
class ComicMatrixFilter : IMatrixFilter
{
/// <summary>
/// Enumurates Argb colour channels.
/// </summary>
private enum ChannelArgb
{
/// <summary>
/// The blue channel
/// </summary>
Blue = 0,
/// <summary>
/// The green channel
/// </summary>
Green = 1,
/// <summary>
/// The red channel
/// </summary>
Red = 2,
/// <summary>
/// The alpha channel
/// </summary>
Alpha = 3
}
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.LoSatch; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
/// <summary>
/// Transfers a single ARGB channel from one image to another.
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="destination">
/// The destination.
/// </param>
/// <param name="sourceChannel">
/// The source channel.
/// </param>
/// <param name="destinationChannel">
/// The destination channel.
/// </param>
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);
}
}
}

85
src/ImageProcessor/Imaging/Filters/GothamMatrixFilter.cs

@ -0,0 +1,85 @@
// -----------------------------------------------------------------------
// <copyright file="GothamMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a gotham filter to an image.
/// </summary>
class GothamMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.Gotham; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

64
src/ImageProcessor/Imaging/Filters/GreyScaleMatrixFilter.cs

@ -0,0 +1,64 @@
// -----------------------------------------------------------------------
// <copyright file="GreyScaleMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a greyscale filter to an image.
/// </summary>
class GreyScaleMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.GreyScale; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

64
src/ImageProcessor/Imaging/Filters/HiSatchMatrixFilter.cs

@ -0,0 +1,64 @@
// -----------------------------------------------------------------------
// <copyright file="HiSatchMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a high saturated filter to an image.
/// </summary>
class HiSatchMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.HiSatch; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

40
src/ImageProcessor/Imaging/Filters/IMatrixFilter.cs

@ -0,0 +1,40 @@
// -----------------------------------------------------------------------
// <copyright file="IMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Imaging.Filters
{
#region Using
using System.Drawing;
using System.Drawing.Imaging;
#endregion
/// <summary>
/// Defines properties and methods for ColorMatrix based filters.
/// </summary>
interface IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.ColoMatrix"/> for this filter instance.
/// </summary>
ColorMatrix Matrix { get; }
#region Methods
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
Image ProcessImage(ImageFactory factory, Image image, Image newImage);
#endregion
}
}

64
src/ImageProcessor/Imaging/Filters/InvertMatrixFilter.cs

@ -0,0 +1,64 @@
// -----------------------------------------------------------------------
// <copyright file="InvertMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add an inverted filter to an image.
/// </summary>
class InvertMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.Invert; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

64
src/ImageProcessor/Imaging/Filters/LoSatchMatrixFilter.cs

@ -0,0 +1,64 @@
// -----------------------------------------------------------------------
// <copyright file="LoSatchMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a low saturated filter to an image.
/// </summary>
class LoSatchMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.LoSatch; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

70
src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs

@ -0,0 +1,70 @@
// -----------------------------------------------------------------------
// <copyright file="LomographMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a lomograph filter to an image.
/// </summary>
class LomographMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.Lomograph; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

99
src/ImageProcessor/Imaging/Filters/PolaroidMatrixFilter.cs

@ -0,0 +1,99 @@
// -----------------------------------------------------------------------
// <copyright file="PolaroidMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a polaroid filter to an image.
/// </summary>
class PolaroidMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.Polaroid; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

64
src/ImageProcessor/Imaging/Filters/SepiaMatrixFilter.cs

@ -0,0 +1,64 @@
// -----------------------------------------------------------------------
// <copyright file="SepiaMatrixFilter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add a sepia filter to an image.
/// </summary>
class SepiaMatrixFilter : IMatrixFilter
{
/// <summary>
/// Gets the <see cref="T:System.Drawing.Imaging.ColoMatrix"/> for this filter instance.
/// </summary>
public ColorMatrix Matrix
{
get { return ColorMatrixes.Sepia; }
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <param name="image">The current image to process</param>
/// <param name="newImage">The new Image to return</param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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;
}
}
}

2
src/ImageProcessor/Imaging/TextLayer.cs

@ -13,7 +13,7 @@ namespace ImageProcessor.Imaging
#endregion
/// <summary>
/// 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.
/// </summary>
public class TextLayer
{

610
src/ImageProcessor/Processors/Copy of Filter.cs

@ -0,0 +1,610 @@
// -----------------------------------------------------------------------
// <copyright file="Filter.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods with which to add filters to an image.
/// </summary>
public class Filter : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"filter=(lomograph|polaroid|blackwhite|sepia|greyscale|gotham|invert|hisatch|losatch|comic)", RegexOptions.Compiled);
/// <summary>
/// Enumurates Argb colour channels.
/// </summary>
private enum ChannelArgb
{
/// <summary>
/// The blue channel
/// </summary>
Blue = 0,
/// <summary>
/// The green channel
/// </summary>
Green = 1,
/// <summary>
/// The red channel
/// </summary>
Red = 2,
/// <summary>
/// The alpha channel
/// </summary>
Alpha = 3
}
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Filter";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Encapsulates methods with which to add filters to an image. e.g polaroid, lomograph";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
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;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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
/// <summary>
/// Transfers a single ARGB channel from one image to another.
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="destination">
/// The destination.
/// </param>
/// <param name="sourceChannel">
/// The source channel.
/// </param>
/// <param name="destinationChannel">
/// The destination channel.
/// </param>
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);
}
/// <summary>
/// A list of available color matrices to apply to an image.
/// </summary>
private static class ColorMatrixes
{
/// <summary>
/// Gets Sepia.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets BlackWhite.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Polaroid.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Lomograph.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets GreyScale.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Gotham.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Invert.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets HiSatch.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets LoSatch.
/// </summary>
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 }
});
}
}
}
}
}

435
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
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"filter=(lomograph|polaroid|blackwhite|sepia|greyscale|gotham|invert|hisatch|losatch|comic)", RegexOptions.Compiled);
/// <summary>
/// Enumurates Argb colour channels.
/// </summary>
private enum ChannelArgb
{
/// <summary>
/// The blue channel
/// </summary>
Blue = 0,
/// <summary>
/// The green channel
/// </summary>
Green = 1,
/// <summary>
/// The red channel
/// </summary>
Red = 2,
/// <summary>
/// The alpha channel
/// </summary>
Alpha = 3
}
#region IGraphicsProcessor Members
/// <summary>
/// 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
/// <summary>
/// Transfers a single ARGB channel from one image to another.
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="destination">
/// The destination.
/// </param>
/// <param name="sourceChannel">
/// The source channel.
/// </param>
/// <param name="destinationChannel">
/// The destination channel.
/// </param>
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);
}
/// <summary>
/// A list of available color matrices to apply to an image.
/// </summary>
private static class ColorMatrixes
{
/// <summary>
/// Gets Sepia.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets BlackWhite.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Polaroid.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Lomograph.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets GreyScale.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Gotham.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets Invert.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets HiSatch.
/// </summary>
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 }
});
}
}
/// <summary>
/// Gets LoSatch.
/// </summary>
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 }
});
}
}
}
}
}

24
src/ImageProcessor/Processors/Resize.cs

@ -24,7 +24,7 @@ namespace ImageProcessor.Processors
/// <summary>
/// The regular expression to search strings for.
/// </summary>
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
/// <summary>
@ -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];

282
src/ImageProcessor/Processors/Rotate.cs

@ -0,0 +1,282 @@
// -----------------------------------------------------------------------
// <copyright file="Rotate.cs" company="James South">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
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
/// <summary>
/// Encapsulates methods to rotate an image.
/// </summary>
public class Rotate : IGraphicsProcessor
{
/// <summary>
/// The regular expression to search strings for.
/// </summary>
//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);
/// <summary>
/// The regular expression to search strings for the angle attribute.
/// </summary>
private static readonly Regex AngleRegex = new Regex(@"rotate=\[-*([1-9][0-7][0-9]|\d{1,2}(?!\d)|180)\]", RegexOptions.Compiled);
/// <summary>
/// The regular expression to search strings for the color attribute.
/// </summary>
private static readonly Regex ColorRegex = new Regex(@"bgcolor-([0-9a-fA-F]{3}){1,2}", RegexOptions.Compiled);
/// <summary>
/// The format of the image to rotate.
/// </summary>
private ImageFormat imageFormat;
#region IGraphicsProcessor Members
/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return "Rotate";
}
}
/// <summary>
/// Gets the description.
/// </summary>
public string Description
{
get
{
return "Rotates an image at the given angle.";
}
}
/// <summary>
/// Gets the regular expression to search strings for.
/// </summary>
public Regex RegexPattern
{
get
{
return QueryRegex;
}
}
/// <summary>
/// Gets or sets DynamicParameter.
/// </summary>
public dynamic DynamicParameter
{
get;
set;
}
/// <summary>
/// Gets the order in which this processor is to be used in a chain.
/// </summary>
public int SortOrder
{
get;
private set;
}
/// <summary>
/// Gets or sets any additional settings required by the processor.
/// </summary>
public Dictionary<string, string> Settings
{
get;
set;
}
/// <summary>
/// The position in the original string where the first character of the captured substring was found.
/// </summary>
/// <param name="queryString">
/// The query string to search.
/// </param>
/// <returns>
/// The zero-based starting position in the original string where the captured substring was found.
/// </returns>
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;
}
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">
/// The the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class containing
/// the image to process.
/// </param>
/// <returns>
/// The processed image from the current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
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
/// <summary>
///
/// </summary>
/// <param name="image"></param>
/// <param name="rotateAtX"></param>
/// <param name="rotateAtY"></param>
/// <param name="angle"></param>
/// <returns></returns>
/// <remarks> Based on http://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations?msg=4155374#xx4155374xx</remarks>
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;
}
}
}
Loading…
Cancel
Save