diff --git a/src/ImageProcessor/Colors/Hsv.cs b/src/ImageProcessor/Colors/Hsv.cs
index d85dd3520..cb71ed3ee 100644
--- a/src/ImageProcessor/Colors/Hsv.cs
+++ b/src/ImageProcessor/Colors/Hsv.cs
@@ -52,7 +52,7 @@ namespace ImageProcessor
/// A value ranging between 0 and 100.
///
public readonly float S;
-
+
///
/// Gets the V value (brightness) component.
/// A value ranging between 0 and 100.
@@ -117,30 +117,33 @@ namespace ImageProcessor
///
public static implicit operator Hsv(Bgra color)
{
- float max = Math.Max(color.R, Math.Max(color.G, color.B));
- float min = Math.Min(color.R, Math.Min(color.G, color.B));
- float delta = max - min;
-
- if (Math.Abs(max) < Epsilon)
+ float r = color.R / 255f;
+ float g = color.G / 255f;
+ float b = color.B / 255f;
+
+ float max = Math.Max(r, Math.Max(g, b));
+ float min = Math.Min(r, Math.Min(g, b));
+ float chroma = max - min;
+ float h = 0;
+ float s = 0;
+ float v = max;
+
+ if (Math.Abs(chroma) < Epsilon)
{
- return new Hsv(0, 0, 0);
+ return new Hsv(0, s * 100, v * 100);
}
- float h = 0.0F;
- float s;
- float v;
-
- if (Math.Abs(delta) < Epsilon) { h = 0; }
- else if (Math.Abs(color.R - max) < Epsilon) { h = (color.G - color.B) / delta; }
- else if (Math.Abs(color.G - max) < Epsilon) { h = 2 + (color.B - color.R) / delta; }
- else if (Math.Abs(color.B - max) < Epsilon) { h = 4 + (color.R - color.G) / delta; }
+ if (Math.Abs(chroma) < Epsilon) { h = 0; }
+ else if (Math.Abs(r - max) < Epsilon) { h = (g - b) / chroma; }
+ else if (Math.Abs(g - max) < Epsilon) { h = 2 + (b - r) / chroma; }
+ else if (Math.Abs(b - max) < Epsilon) { h = 4 + (r - g) / chroma; }
h *= 60;
if (h < 0.0) { h += 360; }
- s = delta / max;
- v = max / 255F;
- return new Hsv(h, s, v);
+ s = (chroma / v);
+
+ return new Hsv(h, s * 100, v * 100);
}
///
@@ -155,9 +158,12 @@ namespace ImageProcessor
///
public static implicit operator Bgra(Hsv color)
{
- if (Math.Abs(color.S) < Epsilon)
+ float s = color.S / 100;
+ float v = color.V / 100;
+
+ if (Math.Abs(s) < Epsilon)
{
- byte component = (byte)(color.V * 255);
+ byte component = (byte)(v * 255);
return new Bgra(component, component, component, 255);
}
@@ -165,51 +171,51 @@ namespace ImageProcessor
int i = (int)(Math.Truncate(h));
float f = h - i;
- float p = color.V * (1.0f - color.S);
- float q = color.V * (1.0f - (color.S * f));
- float t = color.V * (1.0f - (color.S * (1.0f - f)));
+ 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 = color.V;
+ r = v;
g = t;
b = p;
break;
case 1:
r = q;
- g = color.V;
+ g = v;
b = p;
break;
case 2:
r = p;
- g = color.V;
+ g = v;
b = t;
break;
case 3:
r = p;
g = q;
- b = color.V;
+ b = v;
break;
case 4:
r = t;
g = p;
- b = color.V;
+ b = v;
break;
default:
- r = color.V;
+ r = v;
g = p;
b = q;
break;
}
- return new Bgra((byte)b, (byte)g, (byte)r);
+ return new Bgra((byte)(Math.Round(b * 255)), (byte)(Math.Round(g * 255)), (byte)(Math.Round(r * 255)));
}
///
diff --git a/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs b/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
index 788e33a60..c175ba0d0 100644
--- a/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
+++ b/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
@@ -10,6 +10,7 @@
namespace ImageProcessor.Tests
{
+ using System;
using System.Diagnostics.CodeAnalysis;
using Xunit;
@@ -94,13 +95,21 @@ namespace ImageProcessor.Tests
Justification = "Reviewed. Suppression is OK here.")]
public void BgrToHsv()
{
+ // Black
+ Bgra b = new Bgra(0, 0, 0, 255);
+ Hsv h = b;
+
+ Assert.Equal(0, h.H);
+ Assert.Equal(0, h.S);
+ Assert.Equal(0, h.V);
+
// White
Bgra color = new Bgra(255, 255, 255, 255);
Hsv hsv = color;
Assert.Equal(0, hsv.H);
Assert.Equal(0, hsv.S);
- Assert.Equal(1, hsv.V);
+ Assert.Equal(100, hsv.V);
// Dark moderate pink.
Bgra color2 = new Bgra(106, 64, 128, 255);
@@ -109,6 +118,51 @@ namespace ImageProcessor.Tests
Assert.Equal(320.6, hsv2.H, 1);
Assert.Equal(50, hsv2.S, 1);
Assert.Equal(50.2, hsv2.V, 1);
+
+ // Ochre.
+ Bgra color3 = new Bgra(34, 119, 204, 255);
+ Hsv hsv3 = color3;
+
+ Assert.Equal(30, hsv3.H, 1);
+ Assert.Equal(83.3, hsv3.S, 1);
+ Assert.Equal(80, hsv3.V, 1);
+ }
+
+ [Fact]
+ public void HsvToBgr()
+ {
+ // Dark moderate pink.
+ Hsv hsv = new Hsv(320.6f, 50, 50.2f);
+ Bgra bgra = hsv;
+
+ Assert.Equal(bgra.B, 106);
+ Assert.Equal(bgra.G, 64);
+ Assert.Equal(bgra.R, 128);
+
+ // Ochre
+ Hsv hsv2 = new Hsv(30, 83.3f, 80);
+ Bgra bgra2 = hsv2;
+
+ Assert.Equal(bgra2.B, 34);
+ Assert.Equal(bgra2.G, 119);
+ Assert.Equal(bgra2.R, 204);
+
+ // White
+ Hsv hsv3 = new Hsv(0, 0, 100);
+ Bgra bgra3 = hsv3;
+
+ Assert.Equal(bgra3.B, 255);
+ Assert.Equal(bgra3.G, 255);
+ Assert.Equal(bgra3.R, 255);
+
+ // Check others.
+ Random random = new Random(0);
+ for (var i = 0; i < 1000; i++)
+ {
+ Bgra bgra4 = new Bgra((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
+ Hsv hsb4 = bgra4;
+ Assert.Equal(bgra4, (Bgra)hsb4);
+ }
}
}
}