diff --git a/src/ImageProcessor/Common/Extensions/ComparableExtensions.cs b/src/ImageProcessor/Common/Extensions/ComparableExtensions.cs
index 3d82d7a44..5560d90d1 100644
--- a/src/ImageProcessor/Common/Extensions/ComparableExtensions.cs
+++ b/src/ImageProcessor/Common/Extensions/ComparableExtensions.cs
@@ -23,14 +23,15 @@ namespace ImageProcessor
///
public static byte Clamp(this byte value, byte min, byte max)
{
- if (value < min)
+ // Order is important here as someone might set min to higher than max.
+ if (value > max)
{
- return min;
+ return max;
}
- if (value > max)
+ if (value < min)
{
- return max;
+ return min;
}
return value;
@@ -47,14 +48,14 @@ namespace ImageProcessor
///
public static int Clamp(this int value, int min, int max)
{
- if (value < min)
+ if (value > max)
{
- return min;
+ return max;
}
- if (value > max)
+ if (value < min)
{
- return max;
+ return min;
}
return value;
@@ -71,14 +72,14 @@ namespace ImageProcessor
///
public static float Clamp(this float value, float min, float max)
{
- if (value < min)
+ if (value > max)
{
- return min;
+ return max;
}
- if (value > max)
+ if (value < min)
{
- return max;
+ return min;
}
return value;
@@ -95,14 +96,14 @@ namespace ImageProcessor
///
public static double Clamp(this double value, double min, double max)
{
- if (value < min)
+ if (value > max)
{
- return min;
+ return max;
}
- if (value > max)
+ if (value < min)
{
- return max;
+ return min;
}
return value;
diff --git a/src/ImageProcessor/Filters/Hue.cs b/src/ImageProcessor/Filters/Hue.cs
new file mode 100644
index 000000000..b7fafec5a
--- /dev/null
+++ b/src/ImageProcessor/Filters/Hue.cs
@@ -0,0 +1,69 @@
+namespace ImageProcessor.Filters
+{
+ using System;
+ using System.Numerics;
+
+ public class Hue : ColorMatrixFilter
+ {
+ ///
+ /// 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 Hue(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;
+
+ ///
+ 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 lumR = .213f;
+ float lumG = .715f;
+ float lumB = .072f;
+
+ // 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))
+ };
+
+ this.matrix = matrix4X4;
+ }
+ }
+}
diff --git a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
index 14252c394..0c5a843aa 100644
--- a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
+++ b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
@@ -68,10 +68,11 @@ namespace ImageProcessor.Formats
if (header.Length >= 11)
{
+ bool isJfif = IsJfif(header);
+ bool isExif = IsExif(header);
bool isJpeg = IsJpeg(header);
- bool isExif = this.IsExif(header);
- isSupported = isJpeg || isExif;
+ isSupported = isJfif || isExif || isJpeg;
}
return isSupported;
@@ -130,20 +131,20 @@ namespace ImageProcessor.Formats
}
///
- /// Returns a value indicating whether the given bytes identify Jpeg data.
+ /// Returns a value indicating whether the given bytes identify Jfif data.
///
/// The bytes representing the file header.
/// The
- private static bool IsJpeg(byte[] header)
+ private static bool IsJfif(byte[] header)
{
- bool isJpg =
+ bool isJfif =
header[6] == 0x4A && // J
header[7] == 0x46 && // F
header[8] == 0x49 && // I
header[9] == 0x46 && // F
header[10] == 0x00;
- return isJpg;
+ return isJfif;
}
///
@@ -151,7 +152,7 @@ namespace ImageProcessor.Formats
///
/// The bytes representing the file header.
/// The
- private bool IsExif(byte[] header)
+ private static bool IsExif(byte[] header)
{
bool isExif =
header[6] == 0x45 && // E
@@ -162,5 +163,20 @@ namespace ImageProcessor.Formats
return isExif;
}
+
+ ///
+ /// Returns a value indicating whether the given bytes identify Jpeg data.
+ /// This is a last chance resort for jpegs that contain ICC information.
+ ///
+ /// The bytes representing the file header.
+ /// The
+ private static bool IsJpeg(byte[] header)
+ {
+ bool isJpg =
+ header[0] == 0xFF && // 255
+ header[1] == 0xD8; // 216
+
+ return isJpg;
+ }
}
}