ColorUtility.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. using System;
  2. using System.Windows.Media;
  3. namespace TeamAAS.Theme
  4. {
  5. /// <summary>
  6. /// 颜色派生工具:HSL 空间的明度/饱和度调整与对比度计算。
  7. /// 供 ThemeOverlay 做主色派生(hover 变体、暗色提亮、文字对比色),保证专业配色的可读性。
  8. /// </summary>
  9. internal static class ColorUtility
  10. {
  11. /// <summary>降低明度(0~1,如 0.12 = 深 12%),用于 hover/pressed 变体。</summary>
  12. public static Color Darken(Color c, double amount)
  13. {
  14. RgbToHsl(c, out double h, out double s, out double l);
  15. l = Clamp(l - amount);
  16. return HslToRgb(h, s, l, c.A);
  17. }
  18. /// <summary>提高明度(0~1),用于暗色主题下提亮主色保证对比度。</summary>
  19. public static Color Lighten(Color c, double amount)
  20. {
  21. RgbToHsl(c, out double h, out double s, out double l);
  22. l = Clamp(l + amount);
  23. return HslToRgb(h, s, l, c.A);
  24. }
  25. /// <summary>WCAG 相对亮度(0~1),用于对比度判定。</summary>
  26. public static double RelativeLuminance(Color c)
  27. {
  28. double R = Channel(c.R);
  29. double G = Channel(c.G);
  30. double B = Channel(c.B);
  31. return 0.2126 * R + 0.7152 * G + 0.0722 * B;
  32. }
  33. /// <summary>
  34. /// 按背景亮度返回可读的文字色(WCAG AA):亮背景配深字 #212121,暗背景配白字。
  35. /// 用于主色背景上的文字(ReverseTextColor),避免"浅色主色 + 白字"看不清。
  36. /// </summary>
  37. public static Color ContrastText(Color background)
  38. {
  39. return RelativeLuminance(background) > 0.55
  40. ? Color.FromRgb(0x21, 0x21, 0x21)
  41. : Colors.White;
  42. }
  43. private static double Channel(byte v)
  44. {
  45. double d = v / 255.0;
  46. return d <= 0.03928 ? d / 12.92 : Math.Pow((d + 0.055) / 1.055, 2.4);
  47. }
  48. private static void RgbToHsl(Color c, out double h, out double s, out double l)
  49. {
  50. double r = c.R / 255.0;
  51. double g = c.G / 255.0;
  52. double b = c.B / 255.0;
  53. double max = Math.Max(r, Math.Max(g, b));
  54. double min = Math.Min(r, Math.Min(g, b));
  55. l = (max + min) / 2.0;
  56. if (max - min < 1e-9)
  57. {
  58. h = 0; s = 0;
  59. return;
  60. }
  61. double d = max - min;
  62. s = l > 0.5 ? d / (2.0 - max - min) : d / (max + min);
  63. if (max == r) h = (g - b) / d + (g < b ? 6.0 : 0.0);
  64. else if (max == g) h = (b - r) / d + 2.0;
  65. else h = (r - g) / d + 4.0;
  66. h /= 6.0;
  67. }
  68. private static Color HslToRgb(double h, double s, double l, byte alpha)
  69. {
  70. if (s < 1e-9)
  71. {
  72. byte gray = (byte)Math.Round(Clamp(l) * 255.0);
  73. return Color.FromArgb(alpha, gray, gray, gray);
  74. }
  75. double q = l < 0.5 ? l * (1.0 + s) : l + s - l * s;
  76. double p = 2.0 * l - q;
  77. double r = HueToRgb(p, q, h + 1.0 / 3.0);
  78. double g = HueToRgb(p, q, h);
  79. double b = HueToRgb(p, q, h - 1.0 / 3.0);
  80. return Color.FromArgb(
  81. alpha,
  82. (byte)Math.Round(r * 255.0),
  83. (byte)Math.Round(g * 255.0),
  84. (byte)Math.Round(b * 255.0));
  85. }
  86. private static double HueToRgb(double p, double q, double t)
  87. {
  88. if (t < 0.0) t += 1.0;
  89. if (t > 1.0) t -= 1.0;
  90. if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t;
  91. if (t < 1.0 / 2.0) return q;
  92. if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
  93. return p;
  94. }
  95. private static double Clamp(double v)
  96. {
  97. return v < 0.0 ? 0.0 : (v > 1.0 ? 1.0 : v);
  98. }
  99. }
  100. }