diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
index 0def7e5aba..50cdf98fc8 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
@@ -98,6 +98,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToCieLab(xyzColor);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLab ToCieLab(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToCieLab(xyzColor);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
index 5e5bc4ff3d..b46968aa71 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
@@ -85,6 +85,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToCieLch(xyzColor);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToCieLch(xyzColor);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
index 416193da90..3ebed820f1 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
@@ -82,6 +82,20 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieXyy ToCieXyy(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToCieXyy(xyzColor);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
index c910b1cd0f..9c5e382090 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
@@ -100,6 +100,21 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieXyz ToCieXyz(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ // Conversion
+ Rgb rgb = this.ToRgb(color);
+
+ return this.ToCieXyz(rgb);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
index 45223719fd..1363cf8e4c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
@@ -85,6 +85,20 @@ namespace ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Cmyk ToCmyk(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return CmykAndRgbConverter.Convert(rgb);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
index a39b1cd33a..5ad7c990e3 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
@@ -85,6 +85,20 @@ namespace ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsl ToHsl(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return HslAndRgbConverter.Convert(rgb);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
new file mode 100644
index 0000000000..f1f8cf28f2
--- /dev/null
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
@@ -0,0 +1,170 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.ColorSpaces.Conversion
+{
+ using ImageSharp.ColorSpaces;
+ using ImageSharp.ColorSpaces.Conversion.Implementation.Hsv;
+
+ ///
+ /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
+ ///
+ public partial class ColorSpaceConverter
+ {
+ private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter();
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(CieLab color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToHsv(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToHsv(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(CieXyy color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToHsv(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(CieXyz color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return HsvAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(Cmyk color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return HsvAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(Hsl color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return HsvAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(HunterLab color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToHsv(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(LinearRgb color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return HsvAndRgbConverter.Convert(rgb);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(Lms color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+
+ return this.ToHsv(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(Rgb color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ return HsvAndRgbConverter.Convert(color);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Hsv ToHsv(YCbCr color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return HsvAndRgbConverter.Convert(rgb);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
index df62d8bc9b..cad9897fd0 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
@@ -95,6 +95,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public HunterLab ToHunterLab(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToHunterLab(xyzColor);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
index cd0bad073a..cfa7663d23 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
@@ -100,6 +100,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public LinearRgb ToLinearRgb(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+ return this.ToLinearRgb(rgb);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
index c888a9cda3..40b4db9890 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
@@ -90,6 +90,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Lms ToLms(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToLms(xyzColor);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
index e22f230aca..d405bd3651 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
@@ -82,6 +82,19 @@ namespace ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(color);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Rgb ToRgb(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ // Conversion
+ return HsvAndRgbConverter.Convert(color);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
index c670dde9d8..44612c318c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
@@ -99,6 +99,20 @@ namespace ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb);
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public YCbCr ToYCbCr(Hsv color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ Rgb rgb = this.ToRgb(color);
+
+ return YCbCrAndRgbConverter.Convert(rgb);
+ }
+
///
/// Converts a into a
///
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs
new file mode 100644
index 0000000000..54c8e405f0
--- /dev/null
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs
@@ -0,0 +1,130 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.ColorSpaces.Conversion.Implementation.Hsv
+{
+ using System;
+ using System.Runtime.CompilerServices;
+
+ using ImageSharp.ColorSpaces;
+
+ ///
+ /// Color converter between HSV and Rgb
+ /// See for formulas.
+ ///
+ internal class HsvAndRgbConverter : IColorConversion, IColorConversion
+ {
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rgb Convert(Hsv input)
+ {
+ DebugGuard.NotNull(input, nameof(input));
+
+ float s = input.S;
+ float v = input.V;
+
+ if (MathF.Abs(s) < Constants.Epsilon)
+ {
+ return new Rgb(v, v, v);
+ }
+
+ float h = (MathF.Abs(input.H - 360) < Constants.Epsilon) ? 0 : input.H / 60;
+ int i = (int)Math.Truncate(h);
+ float f = h - i;
+
+ float p = v * (1F - s);
+ float q = v * (1F - (s * f));
+ float t = v * (1F - (s * (1F - 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 Rgb(r, g, b);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Hsv Convert(Rgb input)
+ {
+ DebugGuard.NotNull(input, nameof(input));
+
+ float r = input.R;
+ float g = input.G;
+ float b = input.B;
+
+ float max = MathF.Max(r, MathF.Max(g, b));
+ float min = MathF.Min(r, MathF.Min(g, b));
+ float chroma = max - min;
+ float h = 0;
+ float s = 0;
+ float v = max;
+
+ if (MathF.Abs(chroma) < Constants.Epsilon)
+ {
+ return new Hsv(0, s, v);
+ }
+
+ if (MathF.Abs(r - max) < Constants.Epsilon)
+ {
+ h = (g - b) / chroma;
+ }
+ else if (MathF.Abs(g - max) < Constants.Epsilon)
+ {
+ h = 2 + ((b - r) / chroma);
+ }
+ else if (MathF.Abs(b - max) < Constants.Epsilon)
+ {
+ h = 4 + ((r - g) / chroma);
+ }
+
+ h *= 60;
+ if (h < 0.0)
+ {
+ h += 360;
+ }
+
+ s = chroma / v;
+
+ return new Hsv(h, s, v);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs
new file mode 100644
index 0000000000..cef2444480
--- /dev/null
+++ b/src/ImageSharp/ColorSpaces/Hsv.cs
@@ -0,0 +1,233 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.ColorSpaces
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
+ ///
+ public struct Hsv : IColorVector, IEquatable, IAlmostEquatable
+ {
+ ///
+ /// Represents a that has H, S, and V values set to zero.
+ ///
+ public static readonly Hsv Empty = default(Hsv);
+
+ ///
+ /// Max range used for clamping
+ ///
+ private static readonly Vector3 VectorMax = new Vector3(360, 1, 1);
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private readonly Vector3 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The h hue component.
+ /// The s saturation component.
+ /// The v value (brightness) component.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Hsv(float h, float s, float v)
+ : this(new Vector3(h, s, v))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The vector representing the h, s, v components.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Hsv(Vector3 vector)
+ {
+ this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
+ }
+
+ ///
+ /// Gets the hue component.
+ /// A value ranging between 0 and 360.
+ ///
+ public float H
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector.X;
+ }
+
+ ///
+ /// Gets the saturation component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float S
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector.Y;
+ }
+
+ ///
+ /// Gets the value (brightness) component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float V
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector.Z;
+ }
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ public Vector3 Vector
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.backingVector;
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Hsv(Rgba32 color)
+ {
+ float r = color.R / 255F;
+ float g = color.G / 255F;
+ float b = color.B / 255F;
+
+ float max = MathF.Max(r, MathF.Max(g, b));
+ float min = MathF.Min(r, MathF.Min(g, b));
+ float chroma = max - min;
+ float h = 0;
+ float s = 0;
+ float v = max;
+
+ if (MathF.Abs(chroma) < Constants.Epsilon)
+ {
+ return new Hsv(0, s, v);
+ }
+
+ if (MathF.Abs(r - max) < Constants.Epsilon)
+ {
+ h = (g - b) / chroma;
+ }
+ else if (MathF.Abs(g - max) < Constants.Epsilon)
+ {
+ h = 2 + ((b - r) / chroma);
+ }
+ else if (MathF.Abs(b - max) < Constants.Epsilon)
+ {
+ h = 4 + ((r - g) / chroma);
+ }
+
+ h *= 60;
+ if (h < 0.0)
+ {
+ h += 360;
+ }
+
+ s = chroma / v;
+
+ return new Hsv(h, s, v);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Hsv left, Hsv right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Hsv left, Hsv right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.backingVector.GetHashCode();
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "Hsv [ Empty ]";
+ }
+
+ return $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool Equals(object obj)
+ {
+ if (obj is Hsv)
+ {
+ return this.Equals((Hsv)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(Hsv other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool AlmostEquals(Hsv other, float precision)
+ {
+ Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
+
+ return result.X <= precision
+ && result.Y <= precision
+ && result.Z <= precision;
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs
index dd11a09b80..6364c5d5a6 100644
--- a/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs
+++ b/tests/ImageSharp.Tests/Colors/Colorspaces/ColorSpaceEqualityTests.cs
@@ -23,11 +23,12 @@ namespace ImageSharp.Tests.Colors
CieLch.Empty,
CieXyz.Empty,
CieXyy.Empty,
+ Hsl.Empty,
+ Hsl.Empty,
HunterLab.Empty,
Lms.Empty,
LinearRgb.Empty,
Rgb.Empty,
- Hsl.Empty,
YCbCr.Empty
};
@@ -43,6 +44,7 @@ namespace ImageSharp.Tests.Colors
{ new LinearRgb(Vector3.One), new LinearRgb(Vector3.One), typeof(LinearRgb) },
{ new Rgb(Vector3.One), new Rgb(Vector3.One), typeof(Rgb) },
{ new Hsl(Vector3.One), new Hsl(Vector3.One), typeof(Hsl) },
+ { new Hsv(Vector3.One), new Hsv(Vector3.One), typeof(Hsl) },
{ new YCbCr(Vector3.One), new YCbCr(Vector3.One), typeof(YCbCr) },
};
@@ -59,6 +61,7 @@ namespace ImageSharp.Tests.Colors
{ new LinearRgb(Vector3.One), null, typeof(LinearRgb) },
{ new Rgb(Vector3.One), null, typeof(Rgb) },
{ new Hsl(Vector3.One), null, typeof(Hsl) },
+ { new Hsv(Vector3.One), null, typeof(Hsv) },
{ new YCbCr(Vector3.One), null, typeof(YCbCr) },
};
@@ -72,6 +75,7 @@ namespace ImageSharp.Tests.Colors
{ new Rgb(Vector3.One), new Lms(Vector3.Zero), null },
{ new Cmyk(Vector4.One), new Hsl(Vector3.Zero), null },
{ new YCbCr(Vector3.One), new CieXyy(Vector3.Zero), null },
+ { new Hsv(Vector3.One), new Hsl(Vector3.Zero), null },
};
public static readonly TheoryData