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