diff --git a/src/ImageProcessorCore/Common/Extensions/Vector4Extensions.cs b/src/ImageProcessorCore/Common/Extensions/Vector4Extensions.cs
new file mode 100644
index 0000000000..97d2b66d70
--- /dev/null
+++ b/src/ImageProcessorCore/Common/Extensions/Vector4Extensions.cs
@@ -0,0 +1,91 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using System;
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Extension methods for the struct.
+ ///
+ public static class Vector4Extensions
+ {
+ ///
+ /// Compresses a linear color signal to its sRGB equivalent.
+ ///
+ ///
+ ///
+ /// The whose signal to compress.
+ /// The .
+ public static Vector4 Compress(this Vector4 linear)
+ {
+ // TODO: Is there a faster way to do this?
+ float r = Compress(linear.X);
+ float g = Compress(linear.Y);
+ float b = Compress(linear.Z);
+
+ return new Vector4(r, g, b, linear.W);
+ }
+
+ ///
+ /// Expands an sRGB color signal to its linear equivalent.
+ ///
+ ///
+ ///
+ /// The whose signal to expand.
+ /// The .
+ public static Vector4 Expand(this Vector4 gamma)
+ {
+ // TODO: Is there a faster way to do this?
+ float r = Expand(gamma.X);
+ float g = Expand(gamma.Y);
+ float b = Expand(gamma.Z);
+
+ return new Vector4(r, g, b, gamma.W);
+ }
+
+ ///
+ /// Gets the compressed sRGB value from an linear signal.
+ ///
+ ///
+ ///
+ /// The signal value to compress.
+ ///
+ /// The .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float Compress(float signal)
+ {
+ if (signal <= 0.0031308f)
+ {
+ return signal * 12.92f;
+ }
+
+ return (1.055f * (float)Math.Pow(signal, 0.41666666f)) - 0.055f;
+ }
+
+ ///
+ /// Gets the expanded linear value from an sRGB signal.
+ ///
+ ///
+ ///
+ /// The signal value to expand.
+ ///
+ /// The .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float Expand(float signal)
+ {
+ if (signal <= 0.04045f)
+ {
+ return signal / 12.92f;
+ }
+
+ return (float)Math.Pow((signal + 0.055f) / 1.055f, 2.4f);
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/ColorBlindness.cs b/src/ImageProcessorCore/Filters/ColorBlindness.cs
index b5848a5140..6ab553ba2d 100644
--- a/src/ImageProcessorCore/Filters/ColorBlindness.cs
+++ b/src/ImageProcessorCore/Filters/ColorBlindness.cs
@@ -1,14 +1,14 @@
//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
-// -------------------------------------------------------------------------------------------------------------------
+//
namespace ImageProcessorCore
{
using Processors;
///
- /// Extension methods for the type.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
diff --git a/src/ImageProcessorCore/Filters/Lomograph.cs b/src/ImageProcessorCore/Filters/Lomograph.cs
new file mode 100644
index 0000000000..5a484bcc87
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Lomograph.cs
@@ -0,0 +1,58 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the colors of the image recreating an old Lomograph camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image this method extends.
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image Lomograph(this Image source, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ return Lomograph(source, source.Bounds, progressHandler);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Lomograph camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image Lomograph(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ LomographProcessor processor = new LomographProcessor();
+ processor.OnProgress += progressHandler;
+
+ try
+ {
+ return source.Process(rectangle, processor);
+ }
+ finally
+ {
+ processor.OnProgress -= progressHandler;
+ }
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Options/GreyscaleMode.cs b/src/ImageProcessorCore/Filters/Options/GreyscaleMode.cs
new file mode 100644
index 0000000000..269c1179ef
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Options/GreyscaleMode.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ ///
+ /// Provides enumeration over the various greyscale methods available.
+ ///
+ public enum GreyscaleMode
+ {
+ ///
+ /// ITU-R Recommendation BT.709
+ ///
+ Bt709,
+
+ ///
+ /// ITU-R Recommendation BT.601
+ ///
+ Bt601
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Polaroid.cs b/src/ImageProcessorCore/Filters/Polaroid.cs
new file mode 100644
index 0000000000..e56496322c
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Polaroid.cs
@@ -0,0 +1,58 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the colors of the image recreating an old Polaroid camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image this method extends.
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image Polaroid(this Image source, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ return Polaroid(source, source.Bounds, progressHandler);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Polaroid camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image Polaroid(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ PolaroidProcessor processor = new PolaroidProcessor();
+ processor.OnProgress += progressHandler;
+
+ try
+ {
+ return source.Process(rectangle, processor);
+ }
+ finally
+ {
+ processor.OnProgress -= progressHandler;
+ }
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs
index 856ae6d615..9315c0b16a 100644
--- a/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs
+++ b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs
@@ -12,6 +12,8 @@ namespace ImageProcessorCore.Processors
/// . The image will be converted to greyscale before thresholding
/// occurs.
///
+ /// The pixel format.
+ /// The packed format. long, float.
public class ThresholdProcessor : ImageProcessor
where T : IPackedVector
where TP : struct
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs
new file mode 100644
index 0000000000..6893d2a8a0
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs
@@ -0,0 +1,36 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image to their black and white equivalent.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class BlackWhiteProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 1.5f,
+ M12 = 1.5f,
+ M13 = 1.5f,
+ M21 = 1.5f,
+ M22 = 1.5f,
+ M23 = 1.5f,
+ M31 = 1.5f,
+ M32 = 1.5f,
+ M33 = 1.5f,
+ M41 = -1f,
+ M42 = -1f,
+ M43 = -1f,
+ };
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
index cbab86a1c2..dc5811133b 100644
--- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
@@ -29,6 +29,7 @@ namespace ImageProcessorCore.Processors
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
Matrix4x4 matrix = this.Matrix;
+ bool compand = this.Compand;
using (IPixelAccessor sourcePixels = source.Lock())
using (IPixelAccessor targetPixels = target.Lock())
@@ -40,7 +41,7 @@ namespace ImageProcessorCore.Processors
{
for (int x = startX; x < endX; x++)
{
- targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix);
+ targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix, compand);
}
this.OnRowProcessed();
@@ -53,23 +54,23 @@ namespace ImageProcessorCore.Processors
///
/// The source color.
/// The matrix.
+ /// Whether to compand the color during processing.
///
/// The .
///
- private T ApplyMatrix(T color, Matrix4x4 matrix)
+ private T ApplyMatrix(T color, Matrix4x4 matrix, bool compand)
{
- bool compand = this.Compand;
+ Vector4 vector = color.ToVector4();
- //if (compand)
- //{
- // color = Color.Expand(color);
- //}
+ if (compand)
+ {
+ vector = vector.Expand();
+ }
- Vector4 vector = color.ToVector4();
Vector3 transformed = Vector3.Transform(new Vector3(vector.X, vector.Y, vector.Z), matrix);
- //return compand ? Color.Compress(new Color(transformed, color.A)) : new Color(transformed, color.A);
+ vector = new Vector4(transformed.X, transformed.Y, transformed.Z, vector.W);
T packed = default(T);
- packed.PackVector(new Vector4(transformed.X, transformed.Y, transformed.Z, vector.W));
+ packed.PackVector(compand ? vector.Compress() : vector);
return packed;
}
}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs
new file mode 100644
index 0000000000..9e6b25ad68
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs
@@ -0,0 +1,34 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image to greyscale applying the formula as specified by
+ /// ITU-R Recommendation BT.601 .
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class GreyscaleBt601Processor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = .299f,
+ M12 = .299f,
+ M13 = .299f,
+ M21 = .587f,
+ M22 = .587f,
+ M23 = .587f,
+ M31 = .114f,
+ M32 = .114f,
+ M33 = .114f
+ };
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs
new file mode 100644
index 0000000000..771abeab50
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs
@@ -0,0 +1,88 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System;
+ using System.Numerics;
+
+ ///
+ /// An to change the hue of an .
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class HueProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ /// The used to alter the image.
+ ///
+ private Matrix4x4 matrix;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The new brightness of the image. Must be between -100 and 100.
+ public HueProcessor(float angle)
+ {
+ // Wrap the angle round at 360.
+ angle = angle % 360;
+
+ // Make sure it's not negative.
+ while (angle < 0)
+ {
+ angle += 360;
+ }
+
+ this.Angle = angle;
+ }
+
+ ///
+ /// Gets the rotation value.
+ ///
+ public float Angle { get; }
+
+ ///
+ public override Matrix4x4 Matrix => this.matrix;
+
+ ///
+ public override bool Compand => false;
+
+ ///
+ protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
+ {
+ float radians = (float)ImageMaths.DegreesToRadians(this.Angle);
+ double cosradians = Math.Cos(radians);
+ double sinradians = Math.Sin(radians);
+
+ float lumR = .213f;
+ float lumG = .715f;
+ float lumB = .072f;
+
+ float oneMinusLumR = 1 - lumR;
+ float oneMinusLumG = 1 - lumG;
+ float oneMinusLumB = 1 - lumB;
+
+ // The matrix is set up to preserve the luminance of the image.
+ // See http://graficaobscura.com/matrix/index.html
+ // Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx
+ Matrix4x4 matrix4X4 = new Matrix4x4()
+ {
+ M11 = (float)(lumR + (cosradians * oneMinusLumR) - (sinradians * lumR)),
+ M12 = (float)(lumR - (cosradians * lumR) - (sinradians * 0.143)),
+ M13 = (float)(lumR - (cosradians * lumR) - (sinradians * oneMinusLumR)),
+ M21 = (float)(lumG - (cosradians * lumG) - (sinradians * lumG)),
+ M22 = (float)(lumG + (cosradians * oneMinusLumG) + (sinradians * 0.140)),
+ M23 = (float)(lumG - (cosradians * lumG) + (sinradians * lumG)),
+ M31 = (float)(lumB - (cosradians * lumB) + (sinradians * oneMinusLumB)),
+ M32 = (float)(lumB - (cosradians * lumB) - (sinradians * 0.283)),
+ M33 = (float)(lumB + (cosradians * oneMinusLumB) + (sinradians * lumB))
+ };
+
+ this.matrix = matrix4X4;
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs
new file mode 100644
index 0000000000..f7a84fc082
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs
@@ -0,0 +1,30 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating an old Kodachrome camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class KodachromeProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.6997023f,
+ M22 = 0.4609577f,
+ M33 = 0.397218f,
+ M41 = 0.005f,
+ M42 = -0.005f,
+ M43 = 0.005f
+ };
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs
new file mode 100644
index 0000000000..cfdc628935
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating an old Lomograph effect.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class LomographProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 1.5f,
+ M22 = 1.45f,
+ M33 = 1.11f,
+ M41 = -.1f,
+ M42 = .0f,
+ M43 = -.08f
+ };
+
+ ///
+ protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
+ {
+ T packed = default(T);
+ packed.PackBytes(0, 10, 0, 255);
+ new VignetteProcessor { VignetteColor = packed }.Apply(target, target, targetRectangle);
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
new file mode 100644
index 0000000000..7a0263bd05
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
@@ -0,0 +1,54 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating an old Polaroid effect.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class PolaroidProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 1.538f,
+ M12 = -0.062f,
+ M13 = -0.262f,
+ M21 = -0.022f,
+ M22 = 1.578f,
+ M23 = -0.022f,
+ M31 = .216f,
+ M32 = -.16f,
+ M33 = 1.5831f,
+ M41 = 0.02f,
+ M42 = -0.05f,
+ M43 = -0.05f
+ };
+
+ ///
+ protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
+ {
+ T packedV = default(T);
+ packedV.PackBytes(102, 34, 0, 255);
+ new VignetteProcessor { VignetteColor = packedV }.Apply(target, target, targetRectangle);
+
+ T packedG = default(T);
+ packedG.PackBytes(255, 153, 102, 178);
+ new GlowProcessor
+ {
+ GlowColor = packedG,
+ RadiusX = target.Width / 4f,
+ RadiusY = target.Width / 4f
+ }
+ .Apply(target, target, targetRectangle);
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs
new file mode 100644
index 0000000000..39e7b0bd46
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs
@@ -0,0 +1,78 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// An to change the saturation of an .
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class SaturationProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ /// The saturation to be applied to the image.
+ ///
+ private readonly int saturation;
+
+ ///
+ /// The used to alter the image.
+ ///
+ private Matrix4x4 matrix;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The new saturation of the image. Must be between -100 and 100.
+ ///
+ /// is less than -100 or is greater than 100.
+ ///
+ public SaturationProcessor(int saturation)
+ {
+ Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation));
+ this.saturation = saturation;
+ }
+
+ ///
+ public override Matrix4x4 Matrix => this.matrix;
+
+ ///
+ protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
+ {
+ float saturationFactor = this.saturation / 100f;
+
+ // Stop at -1 to prevent inversion.
+ saturationFactor++;
+
+ // The matrix is set up to "shear" the colour space using the following set of values.
+ // Note that each colour component has an effective luminance which contributes to the
+ // overall brightness of the pixel.
+ // See http://graficaobscura.com/matrix/index.html
+ float saturationComplement = 1.0f - saturationFactor;
+ float saturationComplementR = 0.3086f * saturationComplement;
+ float saturationComplementG = 0.6094f * saturationComplement;
+ float saturationComplementB = 0.0820f * saturationComplement;
+
+ Matrix4x4 matrix4X4 = new Matrix4x4()
+ {
+ M11 = saturationComplementR + saturationFactor,
+ M12 = saturationComplementR,
+ M13 = saturationComplementR,
+ M21 = saturationComplementG,
+ M22 = saturationComplementG + saturationFactor,
+ M23 = saturationComplementG,
+ M31 = saturationComplementB,
+ M32 = saturationComplementB,
+ M33 = saturationComplementB + saturationFactor,
+ };
+
+ this.matrix = matrix4X4;
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs
new file mode 100644
index 0000000000..60ba27c0e6
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs
@@ -0,0 +1,37 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image to their sepia equivalent.
+ /// The formula used matches the svg specification.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class SepiaProcessor : ColorMatrixFilter
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = .393f,
+ M12 = .349f,
+ M13 = .272f,
+ M21 = .769f,
+ M22 = .686f,
+ M23 = .534f,
+ M31 = .189f,
+ M32 = .168f,
+ M33 = .131f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs
new file mode 100644
index 0000000000..fdffcd7809
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs
@@ -0,0 +1,80 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System;
+ using System.Numerics;
+ using System.Threading.Tasks;
+
+ ///
+ /// Creates a glow effect on the image
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class GlowProcessor : ImageProcessor
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GlowProcessor()
+ {
+ this.GlowColor.PackVector(Color.White.ToVector4());
+ }
+
+ ///
+ /// Gets or sets the glow color to apply.
+ ///
+ public T GlowColor { get; set; }
+
+ ///
+ /// Gets or sets the the x-radius.
+ ///
+ public float RadiusX { get; set; }
+
+ ///
+ /// Gets or sets the the y-radius.
+ ///
+ public float RadiusY { get; set; }
+
+ ///
+ protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
+ {
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+ T glowColor = this.GlowColor;
+ Vector2 centre = Rectangle.Center(targetRectangle).ToVector2();
+ float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f;
+ float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f;
+ float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY);
+
+ using (IPixelAccessor sourcePixels = source.Lock())
+ using (IPixelAccessor targetPixels = target.Lock())
+ {
+ Parallel.For(
+ startY,
+ endY,
+ y =>
+ {
+ for (int x = startX; x < endX; x++)
+ {
+ // TODO: Premultiply?
+ float distance = Vector2.Distance(centre, new Vector2(x, y));
+ Vector4 sourceColor = sourcePixels[x, y].ToVector4();
+ Vector4 result = Vector4.Lerp(glowColor.ToVector4(), sourceColor, .5f * (distance / maxDistance));
+ T packed = default(T);
+ packed.PackVector(result);
+ targetPixels[x, y] = packed;
+ }
+
+ this.OnRowProcessed();
+ });
+ }
+ }
+ }
+}
+
diff --git a/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs
new file mode 100644
index 0000000000..2afd9e00cd
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs
@@ -0,0 +1,79 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System;
+ using System.Numerics;
+ using System.Threading.Tasks;
+
+ ///
+ /// Creates a vignette effect on the image
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class VignetteProcessor : ImageProcessor
+ where T : IPackedVector
+ where TP : struct
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public VignetteProcessor()
+ {
+ this.VignetteColor.PackVector(Color.Black.ToVector4());
+ }
+
+ ///
+ /// Gets or sets the vignette color to apply.
+ ///
+ public T VignetteColor { get; set; }
+
+ ///
+ /// Gets or sets the the x-radius.
+ ///
+ public float RadiusX { get; set; }
+
+ ///
+ /// Gets or sets the the y-radius.
+ ///
+ public float RadiusY { get; set; }
+
+ ///
+ protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
+ {
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+ T vignetteColor = this.VignetteColor;
+ Vector2 centre = Rectangle.Center(targetRectangle).ToVector2();
+ float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f;
+ float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f;
+ float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY);
+
+ using (IPixelAccessor sourcePixels = source.Lock())
+ using (IPixelAccessor targetPixels = target.Lock())
+ {
+ Parallel.For(
+ startY,
+ endY,
+ y =>
+ {
+ for (int x = startX; x < endX; x++)
+ {
+ float distance = Vector2.Distance(centre, new Vector2(x, y));
+ Vector4 sourceColor = sourcePixels[x, y].ToVector4();
+ Vector4 result = Vector4.Lerp(vignetteColor.ToVector4(), sourceColor, 1 - .9f * (distance / maxDistance));
+ T packed = default(T);
+ packed.PackVector(result);
+ targetPixels[x, y] = packed;
+
+ }
+ this.OnRowProcessed();
+ });
+ }
+ }
+ }
+}
+
diff --git a/src/ImageProcessorCore/Samplers/Crop.cs b/src/ImageProcessorCore/Samplers/Crop.cs
index ff3196c430..04c96dbd1a 100644
--- a/src/ImageProcessorCore/Samplers/Crop.cs
+++ b/src/ImageProcessorCore/Samplers/Crop.cs
@@ -8,7 +8,7 @@ namespace ImageProcessorCore
using Processors;
///
- /// Extension methods for the type.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
diff --git a/src/ImageProcessorCore/Samplers/EntropyCrop.cs b/src/ImageProcessorCore/Samplers/EntropyCrop.cs
index 123378173c..631662d800 100644
--- a/src/ImageProcessorCore/Samplers/EntropyCrop.cs
+++ b/src/ImageProcessorCore/Samplers/EntropyCrop.cs
@@ -8,7 +8,7 @@ namespace ImageProcessorCore
using Processors;
///
- /// Extension methods for the type.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
diff --git a/src/ImageProcessorCore/Samplers/Pad.cs b/src/ImageProcessorCore/Samplers/Pad.cs
index 0b1b959b08..0a7277144e 100644
--- a/src/ImageProcessorCore/Samplers/Pad.cs
+++ b/src/ImageProcessorCore/Samplers/Pad.cs
@@ -8,7 +8,7 @@ namespace ImageProcessorCore
using Processors;
///
- /// Extension methods for the type.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
diff --git a/src/ImageProcessorCore/Samplers/Rotate.cs b/src/ImageProcessorCore/Samplers/Rotate.cs
index 8a53b64981..ad00518f14 100644
--- a/src/ImageProcessorCore/Samplers/Rotate.cs
+++ b/src/ImageProcessorCore/Samplers/Rotate.cs
@@ -8,7 +8,7 @@ namespace ImageProcessorCore
using Processors;
///
- /// Extension methods for the type.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
diff --git a/src/ImageProcessorCore/Samplers/RotateFlip.cs b/src/ImageProcessorCore/Samplers/RotateFlip.cs
index 407737dd37..093af8503f 100644
--- a/src/ImageProcessorCore/Samplers/RotateFlip.cs
+++ b/src/ImageProcessorCore/Samplers/RotateFlip.cs
@@ -8,7 +8,7 @@ namespace ImageProcessorCore
using Processors;
///
- /// Extension methods for the type.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
diff --git a/src/ImageProcessorCore/Samplers/Skew.cs b/src/ImageProcessorCore/Samplers/Skew.cs
index 08e07ffd35..ab188ceeee 100644
--- a/src/ImageProcessorCore/Samplers/Skew.cs
+++ b/src/ImageProcessorCore/Samplers/Skew.cs
@@ -8,7 +8,7 @@ namespace ImageProcessorCore
using Processors;
///
- /// Extension methods for the type.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/ColorBlindnessTest.cs
index 5b2300885f..9e6c9c1cb7 100644
--- a/tests/ImageProcessorCore.Tests/Processors/Filters/ColorBlindnessTest.cs
+++ b/tests/ImageProcessorCore.Tests/Processors/Filters/ColorBlindnessTest.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/LomographTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/LomographTest.cs
new file mode 100644
index 0000000000..e125bd8efd
--- /dev/null
+++ b/tests/ImageProcessorCore.Tests/Processors/Filters/LomographTest.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Tests
+{
+ using System.IO;
+
+ using Xunit;
+
+ public class LomographTest : FileTestBase
+ {
+ [Fact]
+ public void ImageShouldApplyLomographFilter()
+ {
+ const string path = "TestOutput/Lomograph";
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ foreach (string file in Files)
+ {
+ using (FileStream stream = File.OpenRead(file))
+ {
+ string filename = Path.GetFileName(file);
+ Image image = new Image(stream);
+ using (FileStream output = File.OpenWrite($"{path}/{filename}"))
+ {
+ image.Lomograph()
+ .Save(output);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/PolaroidTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/PolaroidTest.cs
new file mode 100644
index 0000000000..b44cbd6d5d
--- /dev/null
+++ b/tests/ImageProcessorCore.Tests/Processors/Filters/PolaroidTest.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Tests
+{
+ using System.IO;
+
+ using Xunit;
+
+ public class PolaroidTest : FileTestBase
+ {
+ [Fact]
+ public void ImageShouldApplyPolaroidFilter()
+ {
+ const string path = "TestOutput/Polaroid";
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ foreach (string file in Files)
+ {
+ using (FileStream stream = File.OpenRead(file))
+ {
+ string filename = Path.GetFileName(file);
+ Image image = new Image(stream);
+ using (FileStream output = File.OpenWrite($"{path}/{filename}"))
+ {
+ image.Polaroid()
+ .Save(output);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file