2025-07-31 20:12:24 +08:00
|
|
|
|
namespace AntDesignWPF
|
2025-07-11 09:20:23 +08:00
|
|
|
|
{
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 提供颜色调色板相关的工具方法,用于生成颜色的色阶变化。
|
|
|
|
|
|
/// 主要用于 Ant Design 主题色系的生成和调整。
|
|
|
|
|
|
/// </summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
public static class ColorPalette
|
|
|
|
|
|
{
|
|
|
|
|
|
#region Fields
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>色相调整步长</summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private const int hueStep = 2;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>饱和度主要调整步长</summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private const int saturationStep = 16;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>饱和度次要调整步长</summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private const int saturationStep2 = 5;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>亮度调整步长(用于浅色)</summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private const int brightnessStep1 = 5;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>亮度调整步长(用于深色)</summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private const int brightnessStep2 = 15;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>浅色色阶数量</summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private const int lightColorCount = 5;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>深色色阶数量</summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private const int darkColorCount = 4;
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Public Methods
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据基准色和索引生成对应的色阶颜色
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="color">基准色</param>
|
|
|
|
|
|
/// <param name="index">色阶索引(1-10),1-6为浅色系,7-10为深色系</param>
|
|
|
|
|
|
/// <returns>生成的色阶颜色</returns>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 计算色相值
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hsv">HSV颜色值</param>
|
|
|
|
|
|
/// <param name="i">调整步数</param>
|
|
|
|
|
|
/// <param name="isLight">是否为浅色系</param>
|
|
|
|
|
|
/// <returns>调整后的色相值</returns>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private static double GetHue(HSV hsv, int i, bool isLight)
|
|
|
|
|
|
{
|
|
|
|
|
|
double hue;
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
// 根据色相范围决定增减方向
|
2025-07-11 09:20:23 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
// 确保色相值在0-360范围内
|
2025-07-11 09:20:23 +08:00
|
|
|
|
if (hue < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
hue += 360;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (hue >= 360)
|
|
|
|
|
|
{
|
|
|
|
|
|
hue -= 360;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Math.Round(hue);
|
|
|
|
|
|
}
|
2025-07-31 20:12:24 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 计算饱和度值
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hsv">HSV颜色值</param>
|
|
|
|
|
|
/// <param name="i">调整步数</param>
|
|
|
|
|
|
/// <param name="isLight">是否为浅色系</param>
|
|
|
|
|
|
/// <returns>调整后的饱和度值(0-100)</returns>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
// 确保饱和度在合理范围内
|
2025-07-11 09:20:23 +08:00
|
|
|
|
if (saturation > 100)
|
|
|
|
|
|
{
|
|
|
|
|
|
saturation = 100;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isLight && i == lightColorCount && saturation > 10)
|
|
|
|
|
|
{
|
|
|
|
|
|
saturation = 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (saturation < 6)
|
|
|
|
|
|
{
|
|
|
|
|
|
saturation = 6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Math.Round(saturation);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 计算明度值
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hsv">HSV颜色值</param>
|
|
|
|
|
|
/// <param name="i">调整步数</param>
|
|
|
|
|
|
/// <param name="isLight">是否为浅色系</param>
|
|
|
|
|
|
/// <returns>调整后的明度值(0-100)</returns>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <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>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
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 };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 将RGB颜色转换为HSV颜色值
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="color">RGB颜色</param>
|
|
|
|
|
|
/// <returns>HSV颜色值</returns>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
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>
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// 将输入值从[0, n]范围转换到[0, 1]范围
|
2025-07-11 09:20:23 +08:00
|
|
|
|
/// </summary>
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <param name="n">输入值</param>
|
|
|
|
|
|
/// <param name="max">最大值</param>
|
|
|
|
|
|
/// <returns>归一化后的值</returns>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
private static double Bound(double n, double max)
|
|
|
|
|
|
{
|
|
|
|
|
|
n = Math.Min(max, Math.Max(0, n));
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
// 处理浮点数舍入误差
|
2025-07-11 09:20:23 +08:00
|
|
|
|
if ((Math.Abs(n - max) < 0.000001))
|
|
|
|
|
|
{
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
// 转换到[0, 1]范围
|
2025-07-11 09:20:23 +08:00
|
|
|
|
return (n % max) / max;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 20:12:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// HSV颜色值结构
|
|
|
|
|
|
/// H: 色相 (0-360)
|
|
|
|
|
|
/// S: 饱和度 (0-1)
|
|
|
|
|
|
/// V: 明度 (0-1)
|
|
|
|
|
|
/// </summary>
|
2025-07-11 09:20:23 +08:00
|
|
|
|
internal struct HSV
|
|
|
|
|
|
{
|
|
|
|
|
|
public double H;
|
|
|
|
|
|
public double S;
|
|
|
|
|
|
public double V;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|