diff --git a/osu.Game/Graphics/HSPAColour.cs b/osu.Game/Graphics/HSPAColour.cs new file mode 100644 index 0000000000..5e3bc29b7e --- /dev/null +++ b/osu.Game/Graphics/HSPAColour.cs @@ -0,0 +1,200 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osuTK.Graphics; + +namespace osu.Game.Graphics +{ + public struct HSPAColour + { + private const float p_r = 0.299f; + private const float p_g = 0.587f; + private const float p_b = 0.114f; + + /// + /// The hue. + /// + public float H; + + /// + /// The saturation. + /// + public float S; + + /// + /// The perceived brightness of this colour. + /// + public float P; + + /// + /// The alpha. + /// + public float A; + + public HSPAColour(float h, float s, float p, float a) + { + H = h; + S = s; + P = p; + A = a; + } + + public HSPAColour(Color4 colour) + { + H = 0; + S = 0; + P = MathF.Sqrt(colour.R * colour.R * p_r + colour.G * colour.G * p_g + colour.B + colour.B * p_b); + A = colour.A; + + if (colour.R == colour.G && colour.R == colour.B) + return; + + if (colour.R >= colour.G && colour.R >= colour.B) + { + if (colour.B >= colour.G) + { + H = 6f / 6f - 1f / 6f * (colour.B - colour.G) / (colour.R - colour.G); + S = 1f - colour.G / colour.R; + } + else + { + H = 0f / 6f + 1f / 6f * (colour.G - colour.B) / (colour.R - colour.B); + S = 1f - colour.B / colour.R; + } + } + else if (colour.G >= colour.R && colour.G >= colour.B) + { + if (colour.R >= colour.B) + { + H = 2f / 6f - 1f / 6f * (colour.R - colour.B) / (colour.G - colour.B); + S = 1f - colour.B / colour.G; + } + else + { + H = 2f / 6f + 1f / 6f * (colour.B - colour.R) / (colour.G - colour.R); + S = 1f - colour.R / colour.G; + } + } + else + { + if (colour.G >= colour.R) + { + H = 4f / 6f - 1f / 6f * (colour.G - colour.R) / (colour.B - colour.R); + S = 1f - colour.R / colour.B; + } + else + { + H = 4f / 6f + 1f / 6f * (colour.R - colour.G) / (colour.B - colour.G); + S = 1f - colour.G / colour.B; + } + } + } + + public Color4 ToColor4() + { + float minOverMax = 1f - S; + + Color4 result = new Color4 { A = A }; + + if (minOverMax > 0f) + { + if (H < 1f / 6f) + { + H = 6f * (H - 0f / 6f); + float part = 1f + H * (1f / minOverMax - 1f); + result.B = P / MathF.Sqrt(p_r / minOverMax / minOverMax + p_g * part * part + p_b); + result.R = result.B / minOverMax; + result.G = result.B + H * (result.R - result.B); + } + else if (H < 2f / 6f) + { + H = 6f * (-H + 2f / 6f); + float part = 1f + H * (1f / minOverMax - 1f); + result.B = P / MathF.Sqrt(p_g / minOverMax / minOverMax + p_r * part * part + p_b); + result.G = result.B / minOverMax; + result.R = result.B + H * (result.G - result.B); + } + else if (H < 3f / 6f) + { + H = 6f * (H - 2f / 6f); + float part = 1f + H * (1f / minOverMax - 1f); + result.R = P / MathF.Sqrt(p_g / minOverMax / minOverMax + p_b * part * part + p_r); + result.G = result.R / minOverMax; + result.B = result.R + H * (result.G - result.R); + } + else if (H < 4f / 6f) + { + H = 6f * (-H + 4f / 6f); + float part = 1f + H * (1f / minOverMax - 1f); + result.R = P / MathF.Sqrt(p_b / minOverMax / minOverMax + p_g * part * part + p_r); + result.B = result.R / minOverMax; + result.G = result.R + H * (result.B - result.R); + } + else if (H < 5f / 6f) + { + H = 6f * (H - 4f / 6f); + float part = 1f + H * (1f / minOverMax - 1f); + result.G = P / MathF.Sqrt(p_b / minOverMax / minOverMax + p_r * part * part + p_g); + result.B = result.G / minOverMax; + result.R = result.G + H * (result.B - result.G); + } + else + { + H = 6f * (-H + 6f / 6f); + float part = 1f + H * (1f / minOverMax - 1f); + result.G = P / MathF.Sqrt(p_r / minOverMax / minOverMax + p_b * part * part + p_g); + result.R = result.G / minOverMax; + result.B = result.G + H * (result.R - result.G); + } + } + else + { + if (H < 1f / 6f) + { + H = 6f * (H - 0f / 6f); + result.R = MathF.Sqrt(P * P / (p_r + p_g * H * H)); + result.G = result.R * H; + result.B = 0f; + } + else if (H < 2f / 6f) + { + H = 6f * (-H + 2f / 6f); + result.G = MathF.Sqrt(P * P / (p_g + p_r * H * H)); + result.R = result.G * H; + result.B = 0f; + } + else if (H < 3f / 6f) + { + H = 6f * (H - 2f / 6f); + result.G = MathF.Sqrt(P * P / (p_g + p_b * H * H)); + result.B = result.G * H; + result.R = 0f; + } + else if (H < 4f / 6f) + { + H = 6f * (-H + 4f / 6f); + result.B = MathF.Sqrt(P * P / (p_b + p_g * H * H)); + result.G = result.B * H; + result.R = 0f; + } + else if (H < 5f / 6f) + { + H = 6f * (H - 4f / 6f); + result.B = MathF.Sqrt(P * P / (p_b + p_r * H * H)); + result.R = result.B * H; + result.G = 0f; + } + else + { + H = 6f * (-H + 6f / 6f); + result.R = MathF.Sqrt(P * P / (p_r + p_b * H * H)); + result.B = result.R * H; + result.G = 0f; + } + } + + return result; + } + } +} diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 0f055856d3..91161d5c71 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -229,177 +229,6 @@ namespace osu.Game.Graphics return Gray(brightness > 0.5f ? 0.2f : 0.9f); } - /// - /// Converts RGBA to HSPA. - /// - public static Color4 ToHSPA(Color4 colour) - { - const float p_r = 0.299f; - const float p_g = 0.587f; - const float p_b = 0.114f; - - Color4 result = new Color4 - { - A = colour.A, - B = MathF.Sqrt(colour.R * colour.R * p_r + colour.G * colour.G * p_g + colour.B + colour.B * p_b) - }; - - if (colour.R == colour.G && colour.R == colour.B) - return result; - - if (colour.R >= colour.G && colour.R >= colour.B) - { - if (colour.B >= colour.G) - { - result.R = 6f / 6f - 1f / 6f * (colour.B - colour.G) / (colour.R - colour.G); - result.G = 1f - colour.G / colour.R; - } - else - { - result.R = 0f / 6f + 1f / 6f * (colour.G - colour.B) / (colour.R - colour.B); - result.G = 1f - colour.B / colour.R; - } - } - else if (colour.G >= colour.R && colour.G >= colour.B) - { - if (colour.R >= colour.B) - { - result.R = 2f / 6f - 1f / 6f * (colour.R - colour.B) / (colour.G - colour.B); - result.G = 1f - colour.B / colour.G; - } - else - { - result.R = 2f / 6f + 1f / 6f * (colour.B - colour.R) / (colour.G - colour.R); - result.G = 1f - colour.R / colour.G; - } - } - else - { - if (colour.G >= colour.R) - { - result.R = 4f / 6f - 1f / 6f * (colour.G - colour.R) / (colour.B - colour.R); - result.G = 1f - colour.R / colour.B; - } - else - { - result.R = 4f / 6f + 1f / 6f * (colour.R - colour.G) / (colour.B - colour.G); - result.G = 1f - colour.G / colour.B; - } - } - - return result; - } - - public static Color4 FromHSPA(Color4 colour) - { - const float p_r = 0.299f; - const float p_g = 0.587f; - const float p_b = 0.114f; - - float minOverMax = 1f - colour.G; - - Color4 result = new Color4 { A = colour.A }; - - if (minOverMax > 0f) - { - if (colour.R < 1f / 6f) - { - colour.R = 6f * (colour.R - 0f / 6f); - float part = 1f + colour.R * (1f / minOverMax - 1f); - result.B = colour.B / MathF.Sqrt(p_r / minOverMax / minOverMax + p_g * part * part + p_b); - result.R = result.B / minOverMax; - result.G = result.B + colour.R * (result.R - result.B); - } - else if (colour.R < 2f / 6f) - { - colour.R = 6f * (-colour.R + 2f / 6f); - float part = 1f + colour.R * (1f / minOverMax - 1f); - result.B = colour.B / MathF.Sqrt(p_g / minOverMax / minOverMax + p_r * part * part + p_b); - result.G = result.B / minOverMax; - result.R = result.B + colour.R * (result.G - result.B); - } - else if (colour.R < 3f / 6f) - { - colour.R = 6f * (colour.R - 2f / 6f); - float part = 1f + colour.R * (1f / minOverMax - 1f); - result.R = colour.B / MathF.Sqrt(p_g / minOverMax / minOverMax + p_b * part * part + p_r); - result.G = result.R / minOverMax; - result.B = result.R + colour.R * (result.G - result.R); - } - else if (colour.R < 4f / 6f) - { - colour.R = 6f * (-colour.R + 4f / 6f); - float part = 1f + colour.R * (1f / minOverMax - 1f); - result.R = colour.B / MathF.Sqrt(p_b / minOverMax / minOverMax + p_g * part * part + p_r); - result.B = result.R / minOverMax; - result.G = result.R + colour.R * (result.B - result.R); - } - else if (colour.R < 5f / 6f) - { - colour.R = 6f * (colour.R - 4f / 6f); - float part = 1f + colour.R * (1f / minOverMax - 1f); - result.G = colour.B / MathF.Sqrt(p_b / minOverMax / minOverMax + p_r * part * part + p_g); - result.B = result.G / minOverMax; - result.R = result.G + colour.R * (result.B - result.G); - } - else - { - colour.R = 6f * (-colour.R + 6f / 6f); - float part = 1f + colour.R * (1f / minOverMax - 1f); - result.G = colour.B / MathF.Sqrt(p_r / minOverMax / minOverMax + p_b * part * part + p_g); - result.R = result.G / minOverMax; - result.B = result.G + colour.R * (result.R - result.G); - } - } - else - { - if (colour.R < 1f / 6f) - { - colour.R = 6f * (colour.R - 0f / 6f); - result.R = MathF.Sqrt(colour.B * colour.B / (p_r + p_g * colour.R * colour.R)); - result.G = result.R * colour.R; - result.B = 0f; - } - else if (colour.R < 2f / 6f) - { - colour.R = 6f * (-colour.R + 2f / 6f); - result.G = MathF.Sqrt(colour.B * colour.B / (p_g + p_r * colour.R * colour.R)); - result.R = result.G * colour.R; - result.B = 0f; - } - else if (colour.R < 3f / 6f) - { - colour.R = 6f * (colour.R - 2f / 6f); - result.G = MathF.Sqrt(colour.B * colour.B / (p_g + p_b * colour.R * colour.R)); - result.B = result.G * colour.R; - result.R = 0f; - } - else if (colour.R < 4f / 6f) - { - colour.R = 6f * (-colour.R + 4f / 6f); - result.B = MathF.Sqrt(colour.B * colour.B / (p_b + p_g * colour.R * colour.R)); - result.G = result.B * colour.R; - result.R = 0f; - } - else if (colour.R < 5f / 6f) - { - colour.R = 6f * (colour.R - 4f / 6f); - result.B = MathF.Sqrt(colour.B * colour.B / (p_b + p_r * colour.R * colour.R)); - result.R = result.B * colour.R; - result.G = 0f; - } - else - { - colour.R = 6f * (-colour.R + 6f / 6f); - result.R = MathF.Sqrt(colour.B * colour.B / (p_r + p_b * colour.R * colour.R)); - result.B = result.R * colour.R; - result.G = 0f; - } - } - - return result; - } - public readonly Color4 TeamColourRed = Color4Extensions.FromHex("#AA1414"); public readonly Color4 TeamColourBlue = Color4Extensions.FromHex("#1462AA"); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 0ef15ed5c2..4ba64687db 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -527,9 +527,7 @@ namespace osu.Game.Rulesets.Objects.Drawables Color4 colour = combo.GetComboColour(CurrentSkin); // Normalise the combo colour to the given brightness level. - colour = OsuColour.ToHSPA(colour); - colour.B = comboColourBrightness.Value; - colour = OsuColour.FromHSPA(colour); + colour = new HSPAColour(colour) { P = comboColourBrightness.Value }.ToColor4(); AccentColour.Value = colour; } diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 44df495929..154ad0fe8c 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -334,6 +334,7 @@ GL GLSL HID + HSPA HSV HTML HUD