Is there an API built into the .NET framework for converting HSV to RGB? I didn't see a method in System.Drawing.Color for this, but it seems surprising that there wouldn't be one in the platform.
I don't think there's a method doing this in the .NET framework.
Check out Converting HSV to RGB colour using C#
This is the implementation code,
void HsvToRgb(double h, double S, double V, out int r, out int g, out int b)
{
double H = h;
while (H < 0) { H += 360; };
while (H >= 360) { H -= 360; };
double R, G, B;
if (V <= 0)
{ R = G = B = 0; }
else if (S <= 0)
{
R = G = B = V;
}
else
{
double hf = H / 60.0;
int i = (int)Math.Floor(hf);
double f = hf - i;
double pv = V * (1 - S);
double qv = V * (1 - S * f);
double tv = V * (1 - S * (1 - f));
switch (i)
{
// Red is the dominant color
case 0:
R = V;
G = tv;
B = pv;
break;
// Green is the dominant color
case 1:
R = qv;
G = V;
B = pv;
break;
case 2:
R = pv;
G = V;
B = tv;
break;
// Blue is the dominant color
case 3:
R = pv;
G = qv;
B = V;
break;
case 4:
R = tv;
G = pv;
B = V;
break;
// Red is the dominant color
case 5:
R = V;
G = pv;
B = qv;
break;
// Just in case we overshoot on our math by a little, we put these here. Since its a switch it won't slow us down at all to put these here.
case 6:
R = V;
G = tv;
B = pv;
break;
case -1:
R = V;
G = pv;
B = qv;
break;
// The color is not defined, we should throw an error.
default:
//LFATAL("i Value error in Pixel conversion, Value is %d", i);
R = G = B = V; // Just pretend its black/white
break;
}
}
r = Clamp((int)(R * 255.0));
g = Clamp((int)(G * 255.0));
b = Clamp((int)(B * 255.0));
}
/// <summary>
/// Clamp a value to 0-255
/// </summary>
int Clamp(int i)
{
if (i < 0) return 0;
if (i > 255) return 255;
return i;
}
Color
, WPF Color
, Gdk# Color
, and others. –
Nursery System.Drawing.Color.FromArgb(255, 0, 0)
, use .GetHue()
etc to emit that to HSV, then use this code to roundtrip back to a Color
, you get rgb 127, 0, 0. .GetBrightness()
returns 0.5, and this code interprets that to mean the dominant color is 255*.5 = 127. At least from my perspective that means this code does not work properly. –
Corrinnecorrival H = (H % 360 + 360) % 360;
be way more efficient than those two while loops? –
Rewarding There isn't a built-in method for doing this, but the calculations aren't terribly complex.
Also note that Color's GetHue(), GetSaturation() and GetBrightness() return HSL values, not HSV.
The following C# code converts between RGB and HSV using the algorithms described on Wikipedia.
I already posted this answer here, but I'll copy the code here for quick reference.
The ranges are 0 - 360 for hue, and 0 - 1 for saturation or value.
public static void ColorToHSV(Color color, out double hue, out double saturation, out double value)
{
int max = Math.Max(color.R, Math.Max(color.G, color.B));
int min = Math.Min(color.R, Math.Min(color.G, color.B));
hue = color.GetHue();
saturation = (max == 0) ? 0 : 1d - (1d * min / max);
value = max / 255d;
}
public static Color ColorFromHSV(double hue, double saturation, double value)
{
int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
double f = hue / 60 - Math.Floor(hue / 60);
value = value * 255;
int v = Convert.ToInt32(value);
int p = Convert.ToInt32(value * (1 - saturation));
int q = Convert.ToInt32(value * (1 - f * saturation));
int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
if (hi == 0)
return Color.FromArgb(255, v, t, p);
else if (hi == 1)
return Color.FromArgb(255, q, v, p);
else if (hi == 2)
return Color.FromArgb(255, p, v, t);
else if (hi == 3)
return Color.FromArgb(255, p, q, v);
else if (hi == 4)
return Color.FromArgb(255, t, p, v);
else
return Color.FromArgb(255, v, p, q);
}
hue = (color.GetHue() + 180) % 360
–
Elysium I don't think there's a method doing this in the .NET framework.
Check out Converting HSV to RGB colour using C#
This is the implementation code,
void HsvToRgb(double h, double S, double V, out int r, out int g, out int b)
{
double H = h;
while (H < 0) { H += 360; };
while (H >= 360) { H -= 360; };
double R, G, B;
if (V <= 0)
{ R = G = B = 0; }
else if (S <= 0)
{
R = G = B = V;
}
else
{
double hf = H / 60.0;
int i = (int)Math.Floor(hf);
double f = hf - i;
double pv = V * (1 - S);
double qv = V * (1 - S * f);
double tv = V * (1 - S * (1 - f));
switch (i)
{
// Red is the dominant color
case 0:
R = V;
G = tv;
B = pv;
break;
// Green is the dominant color
case 1:
R = qv;
G = V;
B = pv;
break;
case 2:
R = pv;
G = V;
B = tv;
break;
// Blue is the dominant color
case 3:
R = pv;
G = qv;
B = V;
break;
case 4:
R = tv;
G = pv;
B = V;
break;
// Red is the dominant color
case 5:
R = V;
G = pv;
B = qv;
break;
// Just in case we overshoot on our math by a little, we put these here. Since its a switch it won't slow us down at all to put these here.
case 6:
R = V;
G = tv;
B = pv;
break;
case -1:
R = V;
G = pv;
B = qv;
break;
// The color is not defined, we should throw an error.
default:
//LFATAL("i Value error in Pixel conversion, Value is %d", i);
R = G = B = V; // Just pretend its black/white
break;
}
}
r = Clamp((int)(R * 255.0));
g = Clamp((int)(G * 255.0));
b = Clamp((int)(B * 255.0));
}
/// <summary>
/// Clamp a value to 0-255
/// </summary>
int Clamp(int i)
{
if (i < 0) return 0;
if (i > 255) return 255;
return i;
}
Color
, WPF Color
, Gdk# Color
, and others. –
Nursery System.Drawing.Color.FromArgb(255, 0, 0)
, use .GetHue()
etc to emit that to HSV, then use this code to roundtrip back to a Color
, you get rgb 127, 0, 0. .GetBrightness()
returns 0.5, and this code interprets that to mean the dominant color is 255*.5 = 127. At least from my perspective that means this code does not work properly. –
Corrinnecorrival H = (H % 360 + 360) % 360;
be way more efficient than those two while loops? –
Rewarding It's not built in, but there's there's an open-source C# library called ColorMine which makes converting between color spaces pretty easy.
Rgb to Hsv:
var rgb = new Rgb {R = 123, G = 11, B = 7};
var hsv = rgb.To<Hsv>();
Hsv to Rgb:
var hsv = new Hsv { H = 360, S = .5, L = .17 }
var rgb = hsv.to<Rgb>();
There is no built-in method (I couldn't find it), but here is code that might help you out. (above solutions didn't work for me)
/// <summary>
/// Converts HSV color values to RGB
/// </summary>
/// <param name="h">0 - 360</param>
/// <param name="s">0 - 100</param>
/// <param name="v">0 - 100</param>
/// <param name="r">0 - 255</param>
/// <param name="g">0 - 255</param>
/// <param name="b">0 - 255</param>
private void HSVToRGB(int h, int s, int v, out int r, out int g, out int b)
{
var rgb = new int[3];
var baseColor = (h + 60) % 360 / 120;
var shift = (h + 60) % 360 - (120 * baseColor + 60 );
var secondaryColor = (baseColor + (shift >= 0 ? 1 : -1) + 3) % 3;
//Setting Hue
rgb[baseColor] = 255;
rgb[secondaryColor] = (int) ((Mathf.Abs(shift) / 60.0f) * 255.0f);
//Setting Saturation
for (var i = 0; i < 3; i++)
rgb[i] += (int) ((255 - rgb[i]) * ((100 - s) / 100.0f));
//Setting Value
for (var i = 0; i < 3; i++)
rgb[i] -= (int) (rgb[i] * (100-v) / 100.0f);
r = rgb[0];
g = rgb[1];
b = rgb[2];
}
I've searched the internet for better way to do this, but I can't find it.
This is a C# method to convert HSV to RGB, the method arguments are in range of 0-1, the output is in range of 0-255
private Color hsv2rgb (float h, float s, float v)
{
Func<float, int> f = delegate (float n)
{
float k = (n + h * 6) % 6;
return (int)((v - (v * s * (Math.Max(0, Math.Min(Math.Min(k, 4 - k), 1))))) * 255);
};
return Color.FromArgb(f(5), f(3), f(1));
}
I have this complex class to convert colors which extending .NET System.Drawing.Color.
There is FromHsv method.
internal static class ColorHelper
{
public static System.Drawing.Color ParseCssColor(string cssColor)
{
cssColor = cssColor.Trim().ToLowerInvariant();
if (cssColor.StartsWith("rgb", StringComparison.Ordinal)) //rgb or rgba
{
int left = cssColor.IndexOf('(');
int right = cssColor.IndexOf(')');
if (left < 0 || right < 0)
{
throw new FormatException("rgba format error");
}
string noBrackets = cssColor.Substring(left + 1, right - left - 1);
string[] parts = noBrackets.Split(',');
int parseRgbValue(string value)
{
return value.IndexOf("%") == -1 ?
int.Parse(value, System.Globalization.CultureInfo.InvariantCulture) :
(int)Math.Round(255f * float.Parse(value.Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture) / 100, MidpointRounding.AwayFromZero);
}
int red = parseRgbValue(parts[0]);
int green = parseRgbValue(parts[1]);
int blue = parseRgbValue(parts[2]);
if (parts.Length == 3)
{
return System.Drawing.Color.FromArgb(red, green, blue);
}
if (parts.Length == 4)
{
float alpha = float.Parse(parts[3], System.Globalization.CultureInfo.InvariantCulture);
return FromRgb(red, green, blue, (int)Math.Round(255f * alpha));
}
}
else if (cssColor.StartsWith("hsl", StringComparison.Ordinal)) //hsl or hsla
{
int left = cssColor.IndexOf('(');
int right = cssColor.IndexOf(')');
if (left < 0 || right < 0)
{
throw new FormatException("hsvl format error");
}
string noBrackets = cssColor.Substring(left + 1, right - left - 1);
string[] parts = noBrackets.Split(',');
float hue = float.Parse(parts[0], System.Globalization.CultureInfo.InvariantCulture);
float saturationPercent = float.Parse(parts[1].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
float lightnessPercent = float.Parse(parts[2].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
if (parts.Length == 3)
{
return FromHsl(hue, saturationPercent, lightnessPercent);
}
if (parts.Length == 4)
{
float alpha = float.Parse(parts[3], System.Globalization.CultureInfo.InvariantCulture);
return FromHsl(hue, saturationPercent, lightnessPercent, (int)Math.Round(255f * alpha));
}
}
else if (cssColor.StartsWith("hsv", StringComparison.Ordinal)) //hsv or hsva
{
int left = cssColor.IndexOf('(');
int right = cssColor.IndexOf(')');
if (left < 0 || right < 0)
{
throw new FormatException("hsva format error");
}
string noBrackets = cssColor.Substring(left + 1, right - left - 1);
string[] parts = noBrackets.Split(',');
float hue = float.Parse(parts[0], System.Globalization.CultureInfo.InvariantCulture);
float saturationPercent = float.Parse(parts[1].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
float valuePercent = float.Parse(parts[2].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
if (parts.Length == 3)
{
return FromHsv(hue, saturationPercent, valuePercent);
}
if (parts.Length == 4)
{
float alpha = float.Parse(parts[3], System.Globalization.CultureInfo.InvariantCulture);
return FromHsv(hue, saturationPercent, valuePercent, (int)Math.Round(255f * alpha));
}
}
//Fallback to ColorTranslator for hex format (#) or named colors, e.g. "Black", "White" etc.
return System.Drawing.ColorTranslator.FromHtml(cssColor);
}
public static System.Drawing.Color FromRgb(int red, int green, int blue, int alpha = 255)
{
return System.Drawing.Color.FromArgb(alpha, red, green, blue);
}
public static System.Drawing.Color FromRgbPercent(float redPercent, float greenPercent, float bluePercent, int alpha = 255)
{
if (redPercent < 0 || redPercent > 100)
{
throw new ArgumentException($"Value of '{redPercent}' is not valid for '{nameof(redPercent)}'. '{nameof(redPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(redPercent));
}
if (greenPercent < 0 || greenPercent > 100)
{
throw new ArgumentException($"Value of '{greenPercent}' is not valid for '{nameof(greenPercent)}'. '{nameof(greenPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(greenPercent));
}
if (bluePercent < 0 || bluePercent > 100)
{
throw new ArgumentException($"Value of '{bluePercent}' is not valid for '{nameof(bluePercent)}'. '{nameof(bluePercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(bluePercent));
}
int red = (int)Math.Round(255f * redPercent / 100, MidpointRounding.AwayFromZero);
int green = (int)Math.Round(255f * greenPercent / 100, MidpointRounding.AwayFromZero);
int blue = (int)Math.Round(255f * bluePercent / 100, MidpointRounding.AwayFromZero);
return System.Drawing.Color.FromArgb(alpha, red, green, blue);
}
public static System.Drawing.Color FromHsl(float hue, float saturationPercent, float lightnessPercent, int alpha = 255)
{
if (hue < 0 || hue > 360)
{
throw new ArgumentException($"Value of '{hue}' is not valid for '{nameof(hue)}'. '{nameof(hue)}' should be greater than or equal to 0 and less than or equal to 360.", nameof(hue));
}
if (saturationPercent < 0 || saturationPercent > 100)
{
throw new ArgumentException($"Value of '{saturationPercent}' is not valid for '{nameof(saturationPercent)}'. '{nameof(saturationPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(saturationPercent));
}
if (lightnessPercent < 0 || lightnessPercent > 100)
{
throw new ArgumentException($"Value of '{lightnessPercent}' is not valid for '{nameof(lightnessPercent)}'. '{nameof(lightnessPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(lightnessPercent));
}
float saturation = saturationPercent / 100f;
float lightness = lightnessPercent / 100f;
hue /= 60f;
float q = (!((double)lightness <= 0.5)) ? (lightness + saturation - (lightness * saturation)) : (lightness * (saturation + 1f));
float p = (lightness * 2f) - q;
float hueToRgb(float p, float q, float t)
{
if (t < 0f)
{
t += 6f;
}
if (t >= 6f)
{
t -= 6f;
}
if (t < 1f)
{
return ((q - p) * t) + p;
}
if (t < 3f)
{
return q;
}
if (t < 4f)
{
return ((q - p) * (4f - t)) + p;
}
return p;
}
return System.Drawing.Color.FromArgb(alpha,
(int)Math.Round(255f * hueToRgb(p, q, hue + 2f), MidpointRounding.AwayFromZero),
(int)Math.Round(255f * hueToRgb(p, q, hue), MidpointRounding.AwayFromZero),
(int)Math.Round(255f * hueToRgb(p, q, hue - 2f), MidpointRounding.AwayFromZero));
}
public static System.Drawing.Color FromHsv(float hue, float saturationPercent, float valuePercent, int alpha = 255)
{
if (hue < 0 || hue > 360)
{
throw new ArgumentException($"Value of '{hue}' is not valid for '{nameof(hue)}'. '{nameof(hue)}' should be greater than or equal to 0 and less than or equal to 360.", nameof(hue));
}
if (saturationPercent < 0 || saturationPercent > 100)
{
throw new ArgumentException($"Value of '{saturationPercent}' is not valid for '{nameof(saturationPercent)}'. '{nameof(saturationPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(saturationPercent));
}
if (valuePercent < 0 || valuePercent > 100)
{
throw new ArgumentException($"Value of '{valuePercent}' is not valid for '{nameof(valuePercent)}'. '{nameof(valuePercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(valuePercent));
}
float saturation = saturationPercent / 100f;
float value = valuePercent / 100f;
float lightness = value - (value * saturation / 2f);
float hslSaturation = lightness == 0f || lightness == 1f ? 0f : (value - lightness) / Math.Min(lightness, 1f - lightness);
return FromHsl(hue, hslSaturation * 100, lightness * 100, alpha);
}
public static (float hue, float saturationPercent, float lightnessPercent) ToHsl(this System.Drawing.Color color)
{
float red = color.R / 255f;
float green = color.G / 255f;
float blue = color.B / 255f;
float vmax = new[] { red, green, blue }.Max();
float vmin = new[] { red, green, blue }.Min();
float d = vmax - vmin;
float hue = 0f;
if (d != 0)
{
if (vmax == red)
{
hue = (green - blue) / d;
}
if (vmax == green)
{
hue = ((blue - red) / d) + 2f;
}
if (vmax == blue)
{
hue = ((red - green) / d) + 4f;
}
}
hue *= 60f;
if (hue < 0f)
{
hue += 360f;
}
float lightness = (vmin + vmax) / 2f;
float saturation = vmin == vmax ? 0f : lightness > 0.5f ? d / (2f - vmax - vmin) : d / (vmax + vmin);
return (hue: (int)Math.Round(hue, MidpointRounding.AwayFromZero), (int)Math.Round(saturation * 100d, MidpointRounding.AwayFromZero), lightnessPercent: (int)Math.Round(lightness * 100d, MidpointRounding.AwayFromZero));
}
public static (float hue, float saturationPercent, float valuePercent) ToHsv(this System.Drawing.Color color)
{
(float hue, float saturationPercent, float lightnessPercent) = ToHsl(color);
float saturation = saturationPercent / 100f;
float lightness = lightnessPercent / 100f;
float value = lightness + (saturation * Math.Min(lightness, 1f - lightness));
float hsvSaturation = value == 0f ? 0f : 2f - (2f * lightness / value);
return (hue, saturationPercent: (int)Math.Round(hsvSaturation * 100d, MidpointRounding.AwayFromZero), valuePercent: (int)Math.Round(value * 100d, MidpointRounding.AwayFromZero));
}
public static string ToHexCssString(this System.Drawing.Color color)
{
if (color.A == byte.MaxValue)
{
return string.Format("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B).ToLowerInvariant();
}
return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", color.A, color.R, color.G, color.B).ToLowerInvariant();
}
public static string ToRgbCssString(this System.Drawing.Color color)
{
if (color.A == byte.MaxValue)
{
return string.Format("rgb({0}, {1}, {2})", color.R, color.G, color.B);
}
return string.Format("rgba({0}, {1}, {2}, {3})", color.R, color.G, color.B, Math.Round((decimal)color.A / 255, 2, MidpointRounding.AwayFromZero).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
public static string ToHslCssString(this System.Drawing.Color color)
{
(float hue, float saturationPercent, float lightnessPercent) = ToHsl(color);
if (color.A == byte.MaxValue)
{
return string.Format("hsl({0}, {1}%, {2}%)", hue, saturationPercent, lightnessPercent);
}
return string.Format("hsla({0}, {1}%, {2}%, {3})", hue, saturationPercent, lightnessPercent, Math.Round((decimal)color.A / 255, 2, MidpointRounding.AwayFromZero).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
public static string ToHsvCssString(this System.Drawing.Color color)
{
(float hue, float saturationPercent, float valuePercent) = ToHsv(color);
if (color.A == byte.MaxValue)
{
return string.Format("hsv({0}, {1}%, {2}%)", hue, saturationPercent, valuePercent);
}
return string.Format("hsva({0}, {1}%, {2}%, {3})", hue, saturationPercent, valuePercent, Math.Round((decimal)color.A / 255, 2, MidpointRounding.AwayFromZero).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
}
© 2022 - 2024 — McMap. All rights reserved.