Files
ShrlAlgoToolkit/AntDesignWPF/ColorPalette.cs
2025-07-31 20:12:24 +08:00

261 lines
7.7 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 AntDesignWPF
{
using System;
using System.Windows.Media;
/// <summary>
/// 提供颜色调色板相关的工具方法,用于生成颜色的色阶变化。
/// 主要用于 Ant Design 主题色系的生成和调整。
/// </summary>
public static class ColorPalette
{
#region Fields
/// <summary>色相调整步长</summary>
private const int hueStep = 2;
/// <summary>饱和度主要调整步长</summary>
private const int saturationStep = 16;
/// <summary>饱和度次要调整步长</summary>
private const int saturationStep2 = 5;
/// <summary>亮度调整步长(用于浅色)</summary>
private const int brightnessStep1 = 5;
/// <summary>亮度调整步长(用于深色)</summary>
private const int brightnessStep2 = 15;
/// <summary>浅色色阶数量</summary>
private const int lightColorCount = 5;
/// <summary>深色色阶数量</summary>
private const int darkColorCount = 4;
#endregion
#region Public Methods
/// <summary>
/// 根据基准色和索引生成对应的色阶颜色
/// </summary>
/// <param name="color">基准色</param>
/// <param name="index">色阶索引(1-10)1-6为浅色系7-10为深色系</param>
/// <returns>生成的色阶颜色</returns>
public static Color Toning(Color color, int index)
{
bool isLight = index <= 6;
HSV hsv = Color2Hsv(color);
int 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>
/// 计算色相值
/// </summary>
/// <param name="hsv">HSV颜色值</param>
/// <param name="i">调整步数</param>
/// <param name="isLight">是否为浅色系</param>
/// <returns>调整后的色相值</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;
}
// 确保色相值在0-360范围内
if (hue < 0)
{
hue += 360;
}
else if (hue >= 360)
{
hue -= 360;
}
return Math.Round(hue);
}
/// <summary>
/// 计算饱和度值
/// </summary>
/// <param name="hsv">HSV颜色值</param>
/// <param name="i">调整步数</param>
/// <param name="isLight">是否为浅色系</param>
/// <returns>调整后的饱和度值(0-100)</returns>
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);
}
/// <summary>
/// 计算明度值
/// </summary>
/// <param name="hsv">HSV颜色值</param>
/// <param name="i">调整步数</param>
/// <param name="isLight">是否为浅色系</param>
/// <returns>调整后的明度值(0-100)</returns>
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;
}
/// <summary>
/// 将HSV颜色值转换为RGB颜色
/// </summary>
/// <param name="h">色相(0-360)</param>
/// <param name="s">饱和度(0-100)</param>
/// <param name="v">明度(0-100)</param>
/// <param name="a">透明度</param>
/// <returns>RGB颜色</returns>
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);
double i = Math.Floor(h);
double f = h - i;
double p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s);
int 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 };
}
/// <summary>
/// 将RGB颜色转换为HSV颜色值
/// </summary>
/// <param name="color">RGB颜色</param>
/// <returns>HSV颜色值</returns>
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;
double 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>
/// 将输入值从[0, n]范围转换到[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));
// 处理浮点数舍入误差
if ((Math.Abs(n - max) < 0.000001))
{
return 1;
}
// 转换到[0, 1]范围
return (n % max) / max;
}
#endregion
}
/// <summary>
/// HSV颜色值结构
/// H: 色相 (0-360)
/// S: 饱和度 (0-1)
/// V: 明度 (0-1)
/// </summary>
internal struct HSV
{
public double H;
public double S;
public double V;
}
}