diff --git a/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs b/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs index 70fbfcddb..db88ca4f3 100644 --- a/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs +++ b/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs @@ -25,7 +25,7 @@ namespace ImageProcessor.UnitTests.Imaging /// Tests the struct equality operators. /// [Test] - public void TestRGBAEquality() + public void TestRgbaEquality() { RgbaColor first = RgbaColor.FromColor(Color.White); RgbaColor second = RgbaColor.FromColor(Color.White); @@ -34,10 +34,10 @@ namespace ImageProcessor.UnitTests.Imaging } /// - /// Tests the struct equality operators. + /// Tests the struct equality operators. /// [Test] - public void TestHSLAEquality() + public void TestHslaEquality() { HslaColor first = HslaColor.FromColor(Color.White); HslaColor second = HslaColor.FromColor(Color.White); @@ -45,6 +45,18 @@ namespace ImageProcessor.UnitTests.Imaging Assert.AreEqual(first, second); } + /// + /// Tests the struct equality operators. + /// + [Test] + public void TestYCbCrEquality() + { + YCbCrColor first = YCbCrColor.FromColor(Color.White); + YCbCrColor second = YCbCrColor.FromColor(Color.White); + + Assert.AreEqual(first, second); + } + /// /// Test conversion to and from a . /// @@ -52,9 +64,13 @@ namespace ImageProcessor.UnitTests.Imaging /// The expected output. /// [Test] + [TestCase("#FFFFFF")] [TestCase("#FEFFFE")] + [TestCase("#F0F8FF")] [TestCase("#000000")] [TestCase("#CCFF33")] + [TestCase("#00FF00")] + [TestCase("#FF00FF")] [TestCase("#990000")] [TestCase("#5C955C")] [TestCase("#5C5C95")] @@ -77,9 +93,13 @@ namespace ImageProcessor.UnitTests.Imaging /// The expected output. /// [Test] + [TestCase("#FFFFFF")] [TestCase("#FEFFFE")] + [TestCase("#F0F8FF")] [TestCase("#000000")] [TestCase("#CCFF33")] + [TestCase("#00FF00")] + [TestCase("#FF00FF")] [TestCase("#990000")] [TestCase("#5C955C")] [TestCase("#5C5C95")] @@ -95,29 +115,33 @@ namespace ImageProcessor.UnitTests.Imaging Assert.AreEqual(expected, result); } - ///// - ///// Test conversion to and from a . - ///// - ///// - ///// The expected output. - ///// - //[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."),Test] - //[TestCase("#FEFFFE")] - //[TestCase("#000000")] - //[TestCase("#CCFF33")] - //[TestCase("#990000")] - //[TestCase("#5C955C")] - //[TestCase("#5C5C95")] - //[TestCase("#3F3F66")] - //[TestCase("#FFFFBB")] - //[TestCase("#FF002B")] - //[TestCase("#00ABFF")] - //public void TestYCbCrConversion(string expected) - //{ - // Color color = ColorTranslator.FromHtml(expected); - // YCbCrColor yCbCrColor = YCbCrColor.FromColor(color); - // string result = ColorTranslator.ToHtml(yCbCrColor); - // Assert.AreEqual(expected, result); - //} + /// + /// Test conversion to and from a . + /// + /// + /// The expected output. + /// + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."), Test] + [TestCase("#FFFFFF")] + [TestCase("#FEFFFE")] + [TestCase("#F0F8FF")] + [TestCase("#000000")] + [TestCase("#CCFF33")] + [TestCase("#00FF00")] + [TestCase("#FF00FF")] + [TestCase("#990000")] + [TestCase("#5C955C")] + [TestCase("#5C5C95")] + [TestCase("#3F3F66")] + [TestCase("#FFFFBB")] + [TestCase("#FF002B")] + [TestCase("#00ABFF")] + public void TestYCbCrConversion(string expected) + { + Color color = ColorTranslator.FromHtml(expected); + YCbCrColor yCbCrColor = YCbCrColor.FromColor(color); + string result = ColorTranslator.ToHtml(yCbCrColor); + Assert.AreEqual(expected, result); + } } } diff --git a/src/ImageProcessor/Common/Extensions/DoubleExtensions.cs b/src/ImageProcessor/Common/Extensions/DoubleExtensions.cs index acc5b12c3..1ce97d2ac 100644 --- a/src/ImageProcessor/Common/Extensions/DoubleExtensions.cs +++ b/src/ImageProcessor/Common/Extensions/DoubleExtensions.cs @@ -10,6 +10,8 @@ namespace ImageProcessor.Common.Extensions { + using System; + /// /// Encapsulates a series of time saving extension methods to the class. /// @@ -30,7 +32,7 @@ namespace ImageProcessor.Common.Extensions /// public static byte ToByte(this double d) { - return (byte)((d > byte.MaxValue) ? byte.MaxValue : ((d < byte.MinValue) ? byte.MinValue : d)); + return Convert.ToByte(Math.Max(0.0d, Math.Min(255d, d))); // ((d > byte.MaxValue) ? byte.MaxValue : ((d < byte.MinValue) ? byte.MinValue : d)); } } } diff --git a/src/ImageProcessor/Imaging/Colors/HSLAColor.cs b/src/ImageProcessor/Imaging/Colors/HSLAColor.cs index d42ee3a87..d989f4ed3 100644 --- a/src/ImageProcessor/Imaging/Colors/HSLAColor.cs +++ b/src/ImageProcessor/Imaging/Colors/HSLAColor.cs @@ -84,6 +84,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the hue component. + /// A value ranging between 0 and 1. /// public float H { @@ -95,6 +96,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the luminosity component. + /// A value ranging between 0 and 1. /// public float L { @@ -106,6 +108,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the saturation component. + /// A value ranging between 0 and 1. /// public float S { @@ -117,6 +120,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the alpha component. + /// A value ranging between 0 and 1. /// public float A { @@ -197,17 +201,11 @@ namespace ImageProcessor.Imaging.Colors /// public static implicit operator HslaColor(Color color) { - HslaColor hslColor = new HslaColor( - color.GetHue() / 360.0f, - color.GetSaturation(), - color.GetBrightness(), - color.A / 255f); - - return hslColor; + return FromColor(color); } /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// /// @@ -218,14 +216,22 @@ namespace ImageProcessor.Imaging.Colors /// public static implicit operator HslaColor(RgbaColor rgbaColor) { - Color color = rgbaColor; - HslaColor hslColor = new HslaColor( - color.GetHue() / 360.0f, - color.GetSaturation(), - color.GetBrightness(), - color.A / 255f); - - return hslColor; + return FromColor(rgbaColor); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator HslaColor(YCbCrColor ycbcrColor) + { + return FromColor(ycbcrColor); } /// @@ -277,29 +283,22 @@ namespace ImageProcessor.Imaging.Colors /// public static implicit operator RgbaColor(HslaColor hslaColor) { - float r = 0, g = 0, b = 0; - if (Math.Abs(hslaColor.l - 0) > .0001) - { - if (Math.Abs(hslaColor.s - 0) <= .0001) - { - r = g = b = hslaColor.l; - } - else - { - float temp2 = GetTemp2(hslaColor); - float temp1 = (2.0f * hslaColor.l) - temp2; - - r = GetColorComponent(temp1, temp2, hslaColor.h + (1.0f / 3.0f)); - g = GetColorComponent(temp1, temp2, hslaColor.h); - b = GetColorComponent(temp1, temp2, hslaColor.h - (1.0f / 3.0f)); - } - } + return RgbaColor.FromColor(hslaColor); + } - return RgbaColor.FromRgba( - Convert.ToByte(255 * r), - Convert.ToByte(255 * g), - Convert.ToByte(255 * b), - Convert.ToByte(255 * hslaColor.a)); + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator YCbCrColor(HslaColor hslaColor) + { + return YCbCrColor.FromColor(hslaColor); } /// diff --git a/src/ImageProcessor/Imaging/Colors/RGBAColor.cs b/src/ImageProcessor/Imaging/Colors/RGBAColor.cs index 38976154a..790e2ae09 100644 --- a/src/ImageProcessor/Imaging/Colors/RGBAColor.cs +++ b/src/ImageProcessor/Imaging/Colors/RGBAColor.cs @@ -191,22 +191,37 @@ namespace ImageProcessor.Imaging.Colors /// public static implicit operator RgbaColor(Color color) { - return new RgbaColor(color); + return FromColor(color); } /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// - /// The instance of to convert. + /// + /// The instance of to convert. /// /// /// An instance of . /// - public static implicit operator RgbaColor(HslaColor color) + public static implicit operator RgbaColor(HslaColor hslaColor) { - return new RgbaColor(color); + return FromColor(hslaColor); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator RgbaColor(YCbCrColor ycbcrColor) + { + return FromColor(ycbcrColor); } /// @@ -239,6 +254,21 @@ namespace ImageProcessor.Imaging.Colors return HslaColor.FromColor(rgba); } + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator YCbCrColor(RgbaColor rgba) + { + return YCbCrColor.FromColor(rgba); + } + /// /// Returns a that represents this instance. /// diff --git a/src/ImageProcessor/Imaging/Colors/YCbCrColor.cs b/src/ImageProcessor/Imaging/Colors/YCbCrColor.cs index d1a5497c5..8f1c001c8 100644 --- a/src/ImageProcessor/Imaging/Colors/YCbCrColor.cs +++ b/src/ImageProcessor/Imaging/Colors/YCbCrColor.cs @@ -4,7 +4,7 @@ // Licensed under the Apache License, Version 2.0. // // -// Represents an YCbCr (luminance, chroma, chroma) color used in digital imaging systems. +// Represents an YCbCr (luminance, chroma, chroma) color conforming to the ITU-R BT.601 standard used in digital imaging systems. // // // -------------------------------------------------------------------------------------------------------------------- @@ -14,10 +14,8 @@ namespace ImageProcessor.Imaging.Colors using System; using System.Drawing; - using ImageProcessor.Common.Extensions; - /// - /// Represents an YCbCr (luminance, chroma, chroma) color used in digital imaging systems. + /// Represents an YCbCr (luminance, chroma, chroma) color conforming to the ITU-R BT.601 standard used in digital imaging systems. /// /// public struct YCbCrColor @@ -50,13 +48,14 @@ namespace ImageProcessor.Imaging.Colors /// The v chroma component. private YCbCrColor(float y, float cb, float cr) { - this.y = y; //Math.Max(0.0f, Math.Min(1.0f, y)); - this.cb = cb; //Math.Max(-0.5f, Math.Min(0.5f, cb)); - this.cr = cr; //Math.Max(-0.5f, Math.Min(0.5f, cr)); + this.y = Math.Max(0f, Math.Min(255f, y)); + this.cb = Math.Max(-255f, Math.Min(255f, cb)); + this.cr = Math.Max(-255f, Math.Min(255f, cr)); } /// /// Gets the Y luminance component. + /// A value ranging between 0 and 255. /// public float Y { @@ -68,23 +67,25 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the U chroma component. + /// A value ranging between -255 and 255. /// public float Cb { get { - return this.y; + return this.cb; } } /// /// Gets the V chroma component. + /// A value ranging between -255 and 255. /// public float Cr { get { - return this.y; + return this.cr; } } @@ -114,12 +115,12 @@ namespace ImageProcessor.Imaging.Colors /// public static YCbCrColor FromColor(Color color) { - float r = color.R; - float g = color.G; - float b = color.B; + byte r = color.R; + byte g = color.G; + byte b = color.B; float y = (float)((0.299 * r) + (0.587 * g) + (0.114 * b)); - float cb = 128 - (float)((-0.168736 * r) - (0.331264 * g) + (0.5 * b)); + float cb = 128 + (float)((-0.168736 * r) - (0.331264 * g) + (0.5 * b)); float cr = 128 + (float)((0.5 * r) - (0.418688 * g) - (0.081312 * b)); return new YCbCrColor(y, cb, cr); @@ -140,24 +141,148 @@ namespace ImageProcessor.Imaging.Colors return FromColor(color); } + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator YCbCrColor(RgbaColor rgbaColor) + { + return FromColor(rgbaColor); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator YCbCrColor(HslaColor hslaColor) + { + return FromColor(hslaColor); + } + /// /// Allows the implicit conversion of an instance of to a /// . /// - /// + /// /// The instance of to convert. /// /// /// An instance of . /// - public static implicit operator Color(YCbCrColor ycbcr) + public static implicit operator Color(YCbCrColor ycbcrColor) { - byte r = Convert.ToInt32(ycbcr.Y + (1.402 * (ycbcr.Cr - 128))).ToByte(); - byte g = Convert.ToInt32(ycbcr.Y - (0.34414 * (ycbcr.Cb - 128)) - (0.71414 * (ycbcr.Cr - 128))).ToByte(); - byte b = Convert.ToInt32(ycbcr.Y + (1.772 * (ycbcr.Cb - 128))).ToByte(); - + float y = ycbcrColor.Y; + float cb = ycbcrColor.Cb - 128; + float cr = ycbcrColor.Cr - 128; + + byte r = Convert.ToByte(Math.Max(0.0f, Math.Min(255f, y + (1.402 * cr)))); + byte g = Convert.ToByte(Math.Max(0.0f, Math.Min(255f, y - (0.34414 * cb) - (0.71414 * cr)))); + byte b = Convert.ToByte(Math.Max(0.0f, Math.Min(255f, y + (1.772 * cb)))); return Color.FromArgb(255, r, g, b); } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator RgbaColor(YCbCrColor ycbcrColor) + { + return RgbaColor.FromColor(ycbcrColor); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator HslaColor(YCbCrColor ycbcrColor) + { + return HslaColor.FromColor(ycbcrColor); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + if (this.IsEmpty()) + { + return "YCbCrColor [Empty]"; + } + + return string.Format("YCbCrColor [ Y={0:#0.##}, Cb={1:#0.##}, Cr={2:#0.##}]", this.Y, this.Cb, this.Cr); + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// + /// true if and this instance are the same type and represent the same value; otherwise, false. + /// + /// Another object to compare to. + public override bool Equals(object obj) + { + if (obj is YCbCrColor) + { + Color thisColor = this; + Color otherColor = (YCbCrColor)obj; + + return thisColor.Equals(otherColor); + } + + return false; + } + + /// + /// Returns the hash code for this instance. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + public override int GetHashCode() + { + Color thisColor = this; + return thisColor.GetHashCode(); + } + + /// + /// Returns a value indicating whether the current instance is empty. + /// + /// + /// The true if this instance is empty; otherwise, false. + /// + private bool IsEmpty() + { + const float Epsilon = .0001f; + return Math.Abs(this.y - 0) <= Epsilon && Math.Abs(this.cb - 0) <= Epsilon && + Math.Abs(this.cr - 0) <= Epsilon; + } } }