diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs
index 4b13fb1dd..b8b554804 100644
--- a/src/ImageProcessor/Colors/Color.cs
+++ b/src/ImageProcessor/Colors/Color.cs
@@ -23,11 +23,30 @@ namespace ImageProcessor
///
public static readonly Color Empty = default(Color);
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.0001f;
+
///
/// The backing vector for SIMD support.
///
private Vector4 backingVector;
+ ///
+ /// Initializes a new instance of the struct with the alpha component set to 1.
+ ///
+ /// The red component of this .
+ /// The green component of this .
+ /// The blue component of this .
+ public Color(float r, float g, float b)
+ : this(r, g, b, 1)
+ {
+ this.backingVector.X = r;
+ this.backingVector.Y = g;
+ this.backingVector.Z = b;
+ }
+
///
/// Initializes a new instance of the struct.
///
@@ -55,12 +74,6 @@ namespace ImageProcessor
this.backingVector = vector;
}
- ///
- /// Gets a value indicating whether this is empty.
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsEmpty => this.backingVector.Equals(default(Vector4));
-
///
/// Gets or sets the red component of the color.
///
@@ -125,6 +138,12 @@ namespace ImageProcessor
}
}
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.backingVector.Equals(default(Vector4));
+
///
/// Gets this color with the component values clamped from 0 to 1.
///
@@ -155,6 +174,77 @@ namespace ImageProcessor
return new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
}
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ ///
+ /// The instance of to convert.
+ ///
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Hsv color)
+ {
+ float s = color.S;
+ float v = color.V;
+
+ if (Math.Abs(s) < Epsilon)
+ {
+ return new Color(v, v, v, 1);
+ }
+
+ float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
+ int i = (int)Math.Truncate(h);
+ float f = h - i;
+
+ float p = v * (1.0f - s);
+ float q = v * (1.0f - (s * f));
+ float t = v * (1.0f - (s * (1.0f - f)));
+
+ float r, g, b;
+ switch (i)
+ {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+
+ default:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ }
+
+ return new Color(r, g, b);
+ }
+
///
/// Computes the product of multiplying a color by a given factor.
///
diff --git a/src/ImageProcessor/Colors/Formats/Bgra32.cs b/src/ImageProcessor/Colors/Formats/Bgra32.cs
index 01e2e664f..f9b10069e 100644
--- a/src/ImageProcessor/Colors/Formats/Bgra32.cs
+++ b/src/ImageProcessor/Colors/Formats/Bgra32.cs
@@ -65,11 +65,6 @@ namespace ImageProcessor
[FieldOffset(0)]
public readonly int BGRA;
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.0001f;
-
///
/// Initializes a new instance of the struct.
///
@@ -194,78 +189,6 @@ namespace ImageProcessor
return new Bgra32((255f * color.B).ToByte(), (255f * color.G).ToByte(), (255f * color.R).ToByte(), (255f * color.A).ToByte());
}
- ///
- /// Allows the implicit conversion of an instance of to a
- /// .
- ///
- ///
- /// The instance of to convert.
- ///
- ///
- /// An instance of .
- ///
- public static implicit operator Bgra32(Hsv color)
- {
- float s = color.S / 100;
- float v = color.V / 100;
-
- if (Math.Abs(s) < Epsilon)
- {
- byte component = (byte)(v * 255);
- return new Bgra32(component, component, component, 255);
- }
-
- float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
- int i = (int)Math.Truncate(h);
- float f = h - i;
-
- float p = v * (1.0f - s);
- float q = v * (1.0f - (s * f));
- float t = v * (1.0f - (s * (1.0f - f)));
-
- float r, g, b;
- switch (i)
- {
- case 0:
- r = v;
- g = t;
- b = p;
- break;
-
- case 1:
- r = q;
- g = v;
- b = p;
- break;
-
- case 2:
- r = p;
- g = v;
- b = t;
- break;
-
- case 3:
- r = p;
- g = q;
- b = v;
- break;
-
- case 4:
- r = t;
- g = p;
- b = v;
- break;
-
- default:
- r = v;
- g = p;
- b = q;
- break;
- }
-
- return new Bgra32((byte)Math.Round(b * 255), (byte)Math.Round(g * 255), (byte)Math.Round(r * 255));
- }
-
///
/// Allows the implicit conversion of an instance of to a
/// .
diff --git a/src/ImageProcessor/Colors/Formats/Hsv.cs b/src/ImageProcessor/Colors/Formats/Hsv.cs
index 942ab8fb7..32aedc96e 100644
--- a/src/ImageProcessor/Colors/Formats/Hsv.cs
+++ b/src/ImageProcessor/Colors/Formats/Hsv.cs
@@ -7,6 +7,7 @@ namespace ImageProcessor
{
using System;
using System.ComponentModel;
+ using System.Numerics;
///
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
@@ -19,27 +20,14 @@ namespace ImageProcessor
public static readonly Hsv Empty = default(Hsv);
///
- /// Gets the H hue component.
- /// A value ranging between 0 and 360.
- ///
- public readonly float H;
-
- ///
- /// Gets the S saturation component.
- /// A value ranging between 0 and 100.
- ///
- public readonly float S;
-
- ///
- /// Gets the V value (brightness) component.
- /// A value ranging between 0 and 100.
+ /// The epsilon for comparing floating point numbers.
///
- public readonly float V;
+ private const float Epsilon = 0.0001f;
///
- /// The epsilon for comparing floating point numbers.
+ /// The backing vector for SIMD support.
///
- private const float Epsilon = 0.0001f;
+ private Vector3 backingVector;
///
/// Initializes a new instance of the struct.
@@ -49,34 +37,49 @@ namespace ImageProcessor
/// The v value (brightness) component.
public Hsv(float h, float s, float v)
{
- this.H = h.Clamp(0, 360);
- this.S = s.Clamp(0, 100);
- this.V = v.Clamp(0, 100);
+ this.backingVector.X = h.Clamp(0, 360);
+ this.backingVector.Y = s.Clamp(0, 1);
+ this.backingVector.Z = v.Clamp(0, 1);
}
+ ///
+ /// Gets the hue component.
+ /// A value ranging between 0 and 360.
+ ///
+ public float H => this.backingVector.X;
+
+ ///
+ /// Gets the saturation component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float S => this.backingVector.Y;
+
+ ///
+ /// Gets the value (brightness) component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float V => this.backingVector.Z;
+
///
/// Gets a value indicating whether this is empty.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsEmpty => Math.Abs(this.H) < Epsilon
- && Math.Abs(this.S) < Epsilon
- && Math.Abs(this.V) < Epsilon;
+ public bool IsEmpty => this.backingVector.Equals(default(Vector3));
///
- /// 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 Hsv(Bgra32 color)
+ public static implicit operator Hsv(Color color)
{
- float r = color.R / 255f;
- float g = color.G / 255f;
- float b = color.B / 255f;
+ color = color.Limited;
+ float r = color.R;
+ float g = color.G;
+ float b = color.B;
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
@@ -87,7 +90,7 @@ namespace ImageProcessor
if (Math.Abs(chroma) < Epsilon)
{
- return new Hsv(0, s * 100, v * 100);
+ return new Hsv(0, s, v);
}
if (Math.Abs(chroma) < Epsilon)
@@ -115,13 +118,11 @@ namespace ImageProcessor
s = chroma / v;
- return new Hsv(h, s * 100, v * 100);
+ return new Hsv(h, s, v);
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , and
- /// properties of the two objects are equal.
+ /// Compares two objects for equality
///
///
/// The on the left side of the operand.
@@ -138,9 +139,7 @@ namespace ImageProcessor
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , and
- /// properties of the two objects are unequal.
+ /// Compares two objects for inequality.
///
///
/// The on the left side of the operand.
@@ -169,37 +168,19 @@ namespace ImageProcessor
{
Hsv color = (Hsv)obj;
- return Math.Abs(this.H - color.H) < Epsilon
- && Math.Abs(this.S - color.S) < Epsilon
- && Math.Abs(this.V - color.V) < Epsilon;
+ return this.backingVector == color.backingVector;
}
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()
{
- unchecked
- {
- int hashCode = this.H.GetHashCode();
- hashCode = (hashCode * 397) ^ this.S.GetHashCode();
- hashCode = (hashCode * 397) ^ this.V.GetHashCode();
- return hashCode;
- }
+ return GetHashCode(this);
}
- ///
- /// Returns the fully qualified type name of this instance.
- ///
- ///
- /// A containing a fully qualified type name.
- ///
+ ///
public override string ToString()
{
if (this.IsEmpty)
@@ -210,18 +191,21 @@ namespace ImageProcessor
return $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
}
+ ///
+ public bool Equals(Hsv other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
///
- /// Indicates whether the current object is equal to another object of the same type.
+ /// Returns the hash code for this instance.
///
+ ///
+ /// The instance of to return the hash code for.
+ ///
///
- /// True if the current object is equal to the parameter; otherwise, false.
+ /// A 32-bit signed integer that is the hash code for this instance.
///
- /// An object to compare with this object.
- public bool Equals(Hsv other)
- {
- return Math.Abs(this.H - other.H) < Epsilon
- && Math.Abs(this.S - other.S) < Epsilon
- && Math.Abs(this.V - other.V) < Epsilon;
- }
+ private static int GetHashCode(Hsv color) => color.backingVector.GetHashCode();
}
}
diff --git a/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs b/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
index 2a822df19..f8087d4d4 100644
--- a/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
+++ b/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
@@ -88,83 +88,83 @@ namespace ImageProcessor.Tests
}
///
- /// Tests the implicit conversion from to .
+ /// Tests the implicit conversion from to .
///
[Fact]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation",
Justification = "Reviewed. Suppression is OK here.")]
- public void BgrToHsv()
+ public void ColorToHsv()
{
// Black
- Bgra32 b = new Bgra32(0, 0, 0, 255);
+ Color b = new Color(0, 0, 0);
Hsv h = b;
- Assert.Equal(0, h.H);
- Assert.Equal(0, h.S);
- Assert.Equal(0, h.V);
+ Assert.Equal(0, h.H, 1);
+ Assert.Equal(0, h.S, 1);
+ Assert.Equal(0, h.V, 1);
// White
- Bgra32 color = new Bgra32(255, 255, 255, 255);
+ Color color = new Color(1, 1, 1);
Hsv hsv = color;
- Assert.Equal(0, hsv.H);
- Assert.Equal(0, hsv.S);
- Assert.Equal(100, hsv.V);
+ Assert.Equal(0f, hsv.H, 1);
+ Assert.Equal(0f, hsv.S, 1);
+ Assert.Equal(1f, hsv.V, 1);
// Dark moderate pink.
- Bgra32 color2 = new Bgra32(106, 64, 128, 255);
+ Color color2 = new Color(128 / 255f, 64 / 255f, 106 / 255f);
Hsv hsv2 = color2;
- Assert.Equal(320.6, hsv2.H, 1);
- Assert.Equal(50, hsv2.S, 1);
- Assert.Equal(50.2, hsv2.V, 1);
+ Assert.Equal(320.6f, hsv2.H, 1);
+ Assert.Equal(0.5f, hsv2.S, 1);
+ Assert.Equal(0.502f, hsv2.V, 2);
// Ochre.
- Bgra32 color3 = new Bgra32(34, 119, 204, 255);
+ Color color3 = new Color(204 / 255f, 119 / 255f, 34 / 255f);
Hsv hsv3 = color3;
- Assert.Equal(30, hsv3.H, 1);
- Assert.Equal(83.3, hsv3.S, 1);
- Assert.Equal(80, hsv3.V, 1);
+ Assert.Equal(30f, hsv3.H, 1);
+ Assert.Equal(0.833f, hsv3.S, 3);
+ Assert.Equal(0.8f, hsv3.V, 1);
}
///
- /// Tests the implicit conversion from to .
+ /// Tests the implicit conversion from to .
///
[Fact]
- public void HsvToBgr()
+ public void HsvToColor()
{
// Dark moderate pink.
- Hsv hsv = new Hsv(320.6f, 50, 50.2f);
- Bgra32 bgra32 = hsv;
+ Hsv hsv = new Hsv(320.6f, 0.5f, 0.502f);
+ Color color = hsv;
- Assert.Equal(bgra32.B, 106);
- Assert.Equal(bgra32.G, 64);
- Assert.Equal(bgra32.R, 128);
+ Assert.Equal(color.B, 106 / 255f, 1);
+ Assert.Equal(color.G, 64 / 255f, 1);
+ Assert.Equal(color.R, 128 / 255f, 1);
// Ochre
- Hsv hsv2 = new Hsv(30, 83.3f, 80);
- Bgra32 bgra2 = hsv2;
+ Hsv hsv2 = new Hsv(30, 0.833f, 0.8f);
+ Color color2 = hsv2;
- Assert.Equal(bgra2.B, 34);
- Assert.Equal(bgra2.G, 119);
- Assert.Equal(bgra2.R, 204);
+ Assert.Equal(color2.B, 34 / 255f, 1);
+ Assert.Equal(color2.G, 119 / 255f, 1);
+ Assert.Equal(color2.R, 204 / 255f, 1);
// White
- Hsv hsv3 = new Hsv(0, 0, 100);
- Bgra32 bgra3 = hsv3;
+ Hsv hsv3 = new Hsv(0, 0, 1);
+ Color color3 = hsv3;
- Assert.Equal(bgra3.B, 255);
- Assert.Equal(bgra3.G, 255);
- Assert.Equal(bgra3.R, 255);
+ Assert.Equal(color3.B, 1, 1);
+ Assert.Equal(color3.G, 1, 1);
+ Assert.Equal(color3.R, 1, 1);
// Check others.
Random random = new Random(0);
for (int i = 0; i < 1000; i++)
{
- Bgra32 bgra4 = new Bgra32((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
- Hsv hsb4 = bgra4;
- Assert.Equal(bgra4, (Bgra32)hsb4);
+ Color color4 = new Color(random.Next(1), random.Next(1), random.Next(1));
+ Hsv hsb4 = color4;
+ Assert.Equal(color4, (Color)hsb4);
}
}