diff --git a/README.md b/README.md
index abd098148..a9ef0f0cc 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Entropy Crop
- Rotation
- [ ] Flip (90, 270, FlipType etc. Need help) [#261](https://github.com/JimBobSquarePants/ImageProcessor/issues/261)
- - [] Rotate by angle (Need help with Paeth approach) [#258](https://github.com/JimBobSquarePants/ImageProcessor/issues/258)
+ - [ ] Rotate by angle (Need help with Paeth approach) [#258](https://github.com/JimBobSquarePants/ImageProcessor/issues/258)
- ColorMatrix operations (Uses Matrix4x4)
- [x] BlackWhite
- [x] Greyscale BT709
@@ -111,7 +111,7 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Brightness
- [x] Pixelate
- [x] Saturation
- - [ ] Hue [#262](https://github.com/JimBobSquarePants/ImageProcessor/issues/262)
+ - [x] Hue
- [x] Blend
- [ ] Mask
- Effects
diff --git a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
index 2e4d598ae..9fc56e72b 100644
--- a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
@@ -16,6 +16,9 @@ namespace ImageProcessor.Filters
///
public abstract Matrix4x4 Matrix { get; }
+ ///
+ public virtual bool Compand => true;
+
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
@@ -34,7 +37,7 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
- target[x, y] = ApplyMatrix(source[x, y], matrix);
+ target[x, y] = this.ApplyMatrix(source[x, y], matrix);
}
}
});
@@ -48,9 +51,14 @@ namespace ImageProcessor.Filters
///
/// The .
///
- private static Color ApplyMatrix(Color color, Matrix4x4 matrix)
+ private Color ApplyMatrix(Color color, Matrix4x4 matrix)
{
- color = Color.InverseCompand(color);
+ bool compand = this.Compand;
+
+ if (compand)
+ {
+ color = Color.InverseCompand(color);
+ }
float sr = color.R;
float sg = color.G;
@@ -60,7 +68,7 @@ namespace ImageProcessor.Filters
color.G = (sr * matrix.M12) + (sg * matrix.M22) + (sb * matrix.M32) + matrix.M42;
color.B = (sr * matrix.M13) + (sg * matrix.M23) + (sb * matrix.M33) + matrix.M43;
- return Color.Compand(color);
+ return compand ? Color.Compand(color) : color;
}
}
}
diff --git a/src/ImageProcessor/Filters/Hue.cs b/src/ImageProcessor/Filters/ColorMatrix/Hue.cs
similarity index 58%
rename from src/ImageProcessor/Filters/Hue.cs
rename to src/ImageProcessor/Filters/ColorMatrix/Hue.cs
index b7fafec5a..3a70f5b49 100644
--- a/src/ImageProcessor/Filters/Hue.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/Hue.cs
@@ -36,31 +36,38 @@
///
public override Matrix4x4 Matrix => this.matrix;
+ ///
+ public override bool Compand => false;
+
///
protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
{
- float degrees = this.Angle;
- double costheta = Math.Cos(degrees);
- double sintheta = Math.Sin(degrees);
+ 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
- // TODO: This isn't correct. Need to double check MS numbers against maths.
Matrix4x4 matrix4X4 = new Matrix4x4()
{
- M11 = (float)(.213 + (costheta * .787) - (sintheta * .213)),
- M12 = (float)(.715 - (costheta * .715) - (sintheta * .715)),
- M13 = (float)(.072 - (costheta * .072) + (sintheta * .928)),
- M21 = (float)(.213 - (costheta * .213) + (sintheta * .143)),
- M22 = (float)(.715 + (costheta * .285) + (sintheta * .140)),
- M23 = (float)(.072 - (costheta * .072) - (sintheta * .283)),
- M31 = (float)(.213 - (costheta * .213) - (sintheta * .787)),
- M32 = (float)(.715 - (costheta * .715) + (sintheta * .715)),
- M33 = (float)(.072 + (costheta * .928) + (sintheta * .072))
+ M11 = (float)(+lumR + (cosradians * oneMinusLumR) - (sinradians * lumR)),
+ M12 = (float)(+lumR - (cosradians * lumR) - (sinradians * lumR)),
+ M13 = (float)(+lumR - (cosradians * lumR) - (sinradians * 0.787)),
+ 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/ImageProcessor/Filters/ColorMatrix/IColorMatrixFilter.cs b/src/ImageProcessor/Filters/ColorMatrix/IColorMatrixFilter.cs
index fb231c5a8..3ac0bedc7 100644
--- a/src/ImageProcessor/Filters/ColorMatrix/IColorMatrixFilter.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/IColorMatrixFilter.cs
@@ -17,5 +17,10 @@ namespace ImageProcessor.Filters
/// Gets the used to alter the image.
///
Matrix4x4 Matrix { get; }
+
+ ///
+ /// Gets a value indicating whether to compand the value on processing.
+ ///
+ bool Compand { get; }
}
}
diff --git a/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs b/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs
index d0cfe6aaf..cab932b25 100644
--- a/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs
@@ -8,7 +8,8 @@ namespace ImageProcessor.Filters
using System.Numerics;
///
- /// Converts the colors of the image to their sepia equivalent recreating an old photo effect.
+ /// Converts the colors of the image to their sepia equivalent.
+ /// The formula used matches the svg specification.
///
public class Sepia : ColorMatrixFilter
{
@@ -25,5 +26,8 @@ namespace ImageProcessor.Filters
M32 = .168f,
M33 = .131f
};
+
+ ///
+ public override bool Compand => false;
}
}
diff --git a/src/ImageProcessor/Filters/ImageFilterExtensions.cs b/src/ImageProcessor/Filters/ImageFilterExtensions.cs
index ceaf47a34..5651934b5 100644
--- a/src/ImageProcessor/Filters/ImageFilterExtensions.cs
+++ b/src/ImageProcessor/Filters/ImageFilterExtensions.cs
@@ -209,6 +209,31 @@ namespace ImageProcessor.Filters
: source.Process(rectangle, new GreyscaleBt601());
}
+ ///
+ /// Alters the hue component of the image.
+ ///
+ /// The image this method extends.
+ /// The angle in degrees to adjust the image.
+ /// The .
+ public static Image Hue(this Image source, float degrees)
+ {
+ return Hue(source, degrees, source.Bounds);
+ }
+
+ ///
+ /// Alters the hue component of the image.
+ ///
+ /// The image this method extends.
+ /// The angle in degrees to adjust the image.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Hue(this Image source, float degrees, Rectangle rectangle)
+ {
+ return source.Process(rectangle, new Hue(degrees));
+ }
+
///
/// Inverts the colors of the image.
///
diff --git a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
index 9c1f76125..e5a0c1a07 100644
--- a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
+++ b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
@@ -41,7 +41,9 @@ namespace ImageProcessor.Tests
{ "Sobel", new Sobel {Greyscale = true} },
{ "Pixelate", new Pixelate(8) },
{ "GuassianBlur", new GuassianBlur(10) },
- { "GuassianSharpen", new GuassianSharpen(10) }
+ { "GuassianSharpen", new GuassianSharpen(10) },
+ { "Hue-180", new Hue(180) },
+ { "Hue--180", new Hue(-180) }
};
[Theory]