btcpayserver/BTCPayServer/Extensions/ColorExtensions.cs

122 lines
3.6 KiB
C#
Raw Normal View History

using System;
using System.Drawing;
namespace BTCPayServer;
public static class ColorExtensions
{
public static double GetLuminance(this Color color)
{
var r = color.R / 255.0;
var g = color.G / 255.0;
var b = color.B / 255.0;
r = (r <= 0.03928) ? r / 12.92 : Math.Pow((r + 0.055) / 1.055, 2.4);
g = (g <= 0.03928) ? g / 12.92 : Math.Pow((g + 0.055) / 1.055, 2.4);
b = (b <= 0.03928) ? b / 12.92 : Math.Pow((b + 0.055) / 1.055, 2.4);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
public static double GetContrastRatio(this Color color1, Color color2)
{
var luminance1 = color1.GetLuminance();
var luminance2 = color2.GetLuminance();
var lighter = Math.Max(luminance1, luminance2);
var darker = Math.Min(luminance1, luminance2);
return (lighter + 0.05) / (darker + 0.05);
}
public static Color GetAdjustedForegroundForBackground(this Color foregroundColor, Color backgroundColor, double lightnessIncrement = 0.01, double threshold = 4.5)
{
var contrastRatio = foregroundColor.GetContrastRatio(backgroundColor);
if (contrastRatio >= threshold) return foregroundColor;
RgbToHsl(foregroundColor, out double hue, out double saturation, out double lightness);
for (int i = 0; i < 100; i++)
{
lightness += lightnessIncrement;
lightness = Math.Clamp(lightness, 0, 1);
Color adjustedColor = HslToRgb(hue, saturation, lightness);
contrastRatio = adjustedColor.GetContrastRatio(backgroundColor);
if (contrastRatio >= threshold) return adjustedColor;
}
return foregroundColor;
}
private static void RgbToHsl(Color color, out double hue, out double saturation, out double lightness)
{
double r = color.R / 255.0;
double g = color.G / 255.0;
double b = color.B / 255.0;
double max = Math.Max(r, Math.Max(g, b));
double min = Math.Min(r, Math.Min(g, b));
lightness = (max + min) / 2.0;
if (max == min)
{
hue = saturation = 0;
}
else
{
double delta = max - min;
saturation = (lightness > 0.5) ? delta / (2.0 - max - min) : delta / (max + min);
if (max == r)
{
hue = (g - b) / delta + (g < b ? 6 : 0);
}
else if (max == g)
{
hue = (b - r) / delta + 2;
}
else
{
hue = (r - g) / delta + 4;
}
hue /= 6;
}
}
private static Color HslToRgb(double hue, double saturation, double lightness)
{
double r, g, b;
if (saturation == 0)
{
r = g = b = lightness;
}
else
{
Func<double, double, double, double> hueToRgb = (p, q, t) =>
{
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6.0) return p + (q - p) * 6 * t;
if (t < 1 / 2.0) return q;
if (t < 2 / 3.0) return p + (q - p) * (2 / 3.0 - t) * 6;
return p;
};
double q = (lightness < 0.5) ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;
double p = 2 * lightness - q;
r = hueToRgb(p, q, hue + 1 / 3.0);
g = hueToRgb(p, q, hue);
b = hueToRgb(p, q, hue - 1 / 3.0);
}
return Color.FromArgb(
(int)Math.Round(r * 255),
(int)Math.Round(g * 255),
(int)Math.Round(b * 255));
}
}