Files
ShrlAlgoToolkit/NeuWPF/NeoUI/Utilities/ColorPalette.cs
ShrlAlgo 955a01f564 整理
2025-08-20 12:10:35 +08:00

207 lines
5.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace NeoUI.Utilities;
public static class ColorPalette
{
#region Fields
private const int hueStep = 2;
private const int saturationStep = 16;
private const int saturationStep2 = 5;
private const int brightnessStep1 = 5;
private const int brightnessStep2 = 15;
private const int lightColorCount = 5;
private const int darkColorCount = 4;
#endregion
#region Public Methods
/// <summary>
/// 根据给定的颜色和索引值,调整颜色的色调、饱和度和亮度。
/// </summary>
/// <param name="color">需要调整的基础颜色。</param>
/// <param name="index">用于确定调整方向和程度的索引值。范围为0到9其中0-6表示向亮色调整7-9表示向暗色调整。</param>
/// <returns>调整后的颜色。</returns>
public static Color Toning(Color color, int index)
{
var isLight = index <= 6;
var hsv = Color2Hsv(color);
var i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
return Hsv2Color(GetHue(hsv, i, isLight), GetSaturation(hsv, i, isLight), GetValue(hsv, i, isLight), color.A);
}
#endregion
#region Private Methods
/// <summary>
/// 根据给定的HSV颜色值、索引和是否为亮色调整计算并返回新的色调值。
/// </summary>
/// <param name="hsv">原始的颜色值以HSV格式表示。</param>
/// <param name="i">用于确定调整程度的索引值。</param>
/// <param name="isLight">布尔值指示调整方向。如果为true则向亮色调整否则向暗色调整。</param>
/// <returns>调整后的色调值范围在0到360之间。</returns>
private static double GetHue(HSV hsv, int i, bool isLight)
{
double hue;
if (hsv.H >= 60 && hsv.H <= 240)
{
hue = isLight ? hsv.H - hueStep * i : hsv.H + hueStep * i;
}
else
{
hue = isLight ? hsv.H + hueStep * i : hsv.H - hueStep * i;
}
if (hue < 0)
{
hue += 360;
}
else if (hue >= 360)
{
hue -= 360;
}
return Math.Round(hue);
}
private static double GetSaturation(HSV hsv, int i, bool isLight)
{
double saturation;
if (isLight)
{
saturation = Math.Round(hsv.S * 100) - saturationStep * i;
}
else if (i == darkColorCount)
{
saturation = Math.Round(hsv.S * 100) + saturationStep;
}
else
{
saturation = Math.Round(hsv.S * 100) + saturationStep2 * i;
}
if (saturation > 100)
{
saturation = 100;
}
if (isLight && i == lightColorCount && saturation > 10)
{
saturation = 10;
}
if (saturation < 6)
{
saturation = 6;
}
return Math.Round(saturation);
}
private static double GetValue(HSV hsv, int i, bool isLight)
{
if (isLight)
{
return Math.Round(hsv.V * 100) + brightnessStep1 * i;
}
return Math.Round(hsv.V * 100) - brightnessStep2 * i;
}
private static Color Hsv2Color(double h, double s, double v, byte a)
{
h = Bound(h, 360) * 6;
s = Bound(s, 100);
v = Bound(v, 100);
var i = Math.Floor(h);
var f = h - i;
double p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s);
var mod = (int)(i % 6);
double r = new double[6] { v, q, p, p, t, v }[mod],
g = new double[6] { t, v, v, q, p, p }[mod],
b = new double[6] { p, p, t, v, v, q }[mod];
r = Math.Min(255, Math.Max(0, r * 255));
g = Math.Min(255, Math.Max(0, g * 255));
b = Math.Min(255, Math.Max(0, b * 255));
return new Color() { R = (byte)Math.Round(r), G = (byte)Math.Round(g), B = (byte)Math.Round(b), A = a };
}
private static HSV Color2Hsv(Color color)
{
double r = Bound(color.R, 255),
g = Bound(color.G, 255),
b = Bound(color.B, 255);
double max = Math.Max(r, Math.Max(g, b)),
min = Math.Min(r, Math.Min(g, b));
double h = 0,
v = max,
d = max - min;
var s = max == 0 ? 0 : d / max;
if (max != min)
{
if (max == r)
{
h = (g - b) / d + (g < b ? 6 : 0);
}
else if (max == g)
{
h = (b - r) / d + 2;
}
else if (max == b)
{
h = (r - g) / d + 4;
}
h /= 6;
}
return new HSV() { H = h * 360, S = s, V = v };
}
/// <summary>
/// Take input from [0, n] and return it as [0, 1]
/// </summary>
/// <param name="n"></param>
/// <param name="max"></param>
/// <returns></returns>
private static double Bound(double n, double max)
{
n = Math.Min(max, Math.Max(0, n));
// Handle floating point rounding errors
if (Math.Abs(n - max) < 0.000001)
{
return 1;
}
// Convert into [0, 1] range if it isn't already
return n % max / max;
}
#endregion
}
internal struct HSV
{
public double H;
public double S;
public double V;
}