-
-
Save profexorgeek/a407c0c96f69a37a2f2554b43491e247 to your computer and use it in GitHub Desktop.
| public struct HSLColor | |
| { | |
| // HSL stands for Hue, Saturation and Luminance. HSL | |
| // color space makes it easier to do calculations | |
| // that operate on these channels | |
| // Helpful color math can be found here: | |
| // https://www.easyrgb.com/en/math.php | |
| /// <summary> | |
| /// Hue: the 'color' of the color! | |
| /// </summary> | |
| public float H; | |
| /// <summary> | |
| /// Saturation: How grey or vivid/colorful a color is | |
| /// </summary> | |
| public float S; | |
| /// <summary> | |
| /// Luminance: The brightness or lightness of the color | |
| /// </summary> | |
| public float L; | |
| public HSLColor(float h, float s, float l) | |
| { | |
| H = h; | |
| S = s; | |
| L = l; | |
| } | |
| public static HSLColor FromColor(Color color) | |
| { | |
| return FromRgb(color.R, color.G, color.B); | |
| } | |
| public static HSLColor FromRgb(byte R, byte G, byte B) | |
| { | |
| var hsl = new HSLColor(); | |
| hsl.H = 0; | |
| hsl.S = 0; | |
| hsl.L = 0; | |
| float r = R / 255f; | |
| float g = G / 255f; | |
| float b = B / 255f; | |
| float min = Math.Min(Math.Min(r, g), b); | |
| float max = Math.Max(Math.Max(r, g), b); | |
| float delta = max - min; | |
| // luminance is the ave of max and min | |
| hsl.L = (max + min) / 2f; | |
| if (delta > 0) | |
| { | |
| if (hsl.L < 0.5f) | |
| { | |
| hsl.S = delta / (max + min); | |
| } | |
| else | |
| { | |
| hsl.S = delta / (2 - max - min); | |
| } | |
| float deltaR = (((max - r) / 6f) + (delta / 2f)) / delta; | |
| float deltaG = (((max - g) / 6f) + (delta / 2f)) / delta; | |
| float deltaB = (((max - b) / 6f) + (delta / 2f)) / delta; | |
| if (r == max) | |
| { | |
| hsl.H = deltaB - deltaG; | |
| } | |
| else if (g == max) | |
| { | |
| hsl.H = (1f / 3f) + deltaR - deltaB; | |
| } | |
| else if (b == max) | |
| { | |
| hsl.H = (2f / 3f) + deltaG - deltaR; | |
| } | |
| if (hsl.H < 0) | |
| { | |
| hsl.H += 1; | |
| } | |
| if (hsl.H > 1) | |
| { | |
| hsl.H -= 1; | |
| } | |
| } | |
| return hsl; | |
| } | |
| public HSLColor GetComplement() | |
| { | |
| // complementary colors are across the color wheel | |
| // which is 180 degrees or 50% of the way around the | |
| // wheel. Add 50% to our hue and wrap large/small values | |
| var h = H + 0.5f; | |
| if (h > 1) | |
| { | |
| h -= 1; | |
| } | |
| return new HSLColor(h, S, L); | |
| } | |
| public Color ToRgbColor() | |
| { | |
| var c = new Color(); | |
| if (S == 0) | |
| { | |
| c.R = (byte)(L * 255f); | |
| c.G = (byte)(L * 255f); | |
| c.B = (byte)(L * 255f); | |
| } | |
| else | |
| { | |
| float v2 = (L + S) - (S * L); | |
| if (L < 0.5f) | |
| { | |
| v2 = L * (1 + S); | |
| } | |
| float v1 = 2f * L - v2; | |
| c.R = (byte)(255f * HueToRgb(v1, v2, H + (1f / 3f))); | |
| c.G = (byte)(255f * HueToRgb(v1, v2, H)); | |
| c.B = (byte)(255f * HueToRgb(v1, v2, H - (1f / 3f))); | |
| } | |
| return c; | |
| } | |
| private static float HueToRgb(float v1, float v2, float vH) | |
| { | |
| vH += (vH < 0) ? 1 : 0; | |
| vH -= (vH > 1) ? 1 : 0; | |
| float ret = v1; | |
| if ((6 * vH) < 1) | |
| { | |
| ret = (v1 + (v2 - v1) * 6 * vH); | |
| } | |
| else if ((2 * vH) < 1) | |
| { | |
| ret = (v2); | |
| } | |
| else if ((3 * vH) < 2) | |
| { | |
| ret = (v1 + (v2 - v1) * ((2f / 3f) - vH) * 6f); | |
| } | |
| return ret.Clamp(0, 1); | |
| } | |
| } |
Can you show me how you are using it, and are you using this with MonoGame? This should work (may be minor syntax errors since I just wrote this in the browser):
var hslRed = HSLColor.FromColor(Color.Red);
var hslGreen = hslRed.GetComplement();
var greenColor = hslGreen.ToRgbColor();
I use this class extensively in my Steam game Masteroid so I know it works well with MonoGame. The Color object this is designed to work with is specifically the Microsoft.Framework.Xna.Color, not a standard .NET framework color!
Can you give me some sample values you used to initialize the HSLColor object? There are two things that could be happening here:
- You are initializing the HSLColor with a Luminosity value of 1: this will always result in white.
- You are using/casting to a different type of color object or other settings in your project are changing how premultiplied alpha works. Premultiplied alpha can cause a variety of unexpected conversion problems.
I have double checked the initialization and conversion and they seem to match what I've been using in my project for a few years now. It's working fine in my project so I suspect it's one of these two things. I'd love to figure this out if you have time to test or provide any more specific information. If I get a chance and remember, I'll try creating a small sample project and pushing it to github for demonstration.
This gives me a white color for me no matter what I set. I tried every possible H/S/L combination.