从 HSL 值创建 C# 颜色
Posted
技术标签:
【中文标题】从 HSL 值创建 C# 颜色【英文标题】:Creating a C# Color from HSL values 【发布时间】:2011-05-04 12:11:11 【问题描述】:如何?
【问题讨论】:
另见:***.com/questions/2942/hsl-in-net/2504318#2504318 【参考方案1】:我有一个进行 RGB、HSB 和 HSL 转换的类。我不是色彩专家,我也没有声称这个类具有最快的算法(它在一些地方使用 string.Convert 以确保浮点转换按预期进行 --- 现在可以重新访问一些新的 BCL 浮点转换更改 ...)
然而,这个类已经过测试并且工作了一段时间,并且似乎可以正确地完成工作。我用它来创建 Frozen Wpf Brushes,它们不会在紧密循环中重复运行(或在某些性能关键的渲染路径中);结果似乎和预期的一样。
如果您有严重的问题,您应该检查代码......这里是:
using System;
using System.Drawing;
using SystemMath = System.Math;
namespace Sc.Util.Rendering
/// <summary>
/// Static methods for transforming argb spaces and argb values.
/// </summary>
public static class SimpleColorTransforms
private static double tolerance
=> 0.000000000000001;
/// <summary>
/// Defines brightness levels.
/// </summary>
public enum Brightness
: byte
Bright = 255,
MediumBright = 210,
Medium = 142,
Dim = 98,
XDim = 50
/// <summary>
/// Defines alpha levels.
/// </summary>
public enum Alpha
: byte
Opaque = 255,
MediumHigh = 230,
Medium = 175,
MediumLow = 142,
Low = 109,
XLow = 45
/// <summary>
/// Defines hint alpha levels.
/// </summary>
public enum HintAlpha
: byte
Low = 64,
XLow = 48,
XxLow = 32,
XxxLow = 16
/// <summary>
/// Specifies a mode for argb transformations.
/// </summary>
public enum ColorTransformMode
: byte
Hsl,
Hsb
/// <summary>
/// Converts RGB to HSL. Alpha is ignored.
/// Output is: H: [0, 360], S: [0, 1], L: [0, 1] .
/// </summary>
/// <param name="color">The color to convert.</param>
public static double[] RgBtoHsl(Color color)
double h = 0D;
double s = 0D;
double l;
// normalize red, green, blue values
double r = color.R / 255D;
double g = color.G / 255D;
double b = color.B / 255D;
double max = SystemMath.Max(r, SystemMath.Max(g, b));
double min = SystemMath.Min(r, SystemMath.Min(g, b));
// hue
if (SystemMath.Abs(max - min) < SimpleColorTransforms.tolerance)
h = 0D; // undefined
else if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
&& (g >= b))
h = (60D * (g - b)) / (max - min);
else if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
&& (g < b))
h = ((60D * (g - b)) / (max - min)) + 360D;
else if (SystemMath.Abs(max - g) < SimpleColorTransforms.tolerance)
h = ((60D * (b - r)) / (max - min)) + 120D;
else if (SystemMath.Abs(max - b) < SimpleColorTransforms.tolerance)
h = ((60D * (r - g)) / (max - min)) + 240D;
// luminance
l = (max + min) / 2D;
// saturation
if ((SystemMath.Abs(l) < SimpleColorTransforms.tolerance)
|| (SystemMath.Abs(max - min) < SimpleColorTransforms.tolerance))
s = 0D;
else if ((0D < l)
&& (l <= .5D))
s = (max - min) / (max + min);
else if (l > .5D)
s = (max - min) / (2D - (max + min)); //(max-min > 0)?
return new[]
SystemMath.Max(0D, SystemMath.Min(360D, double.Parse($"h:0.##"))),
SystemMath.Max(0D, SystemMath.Min(1D, double.Parse($"s:0.##"))),
SystemMath.Max(0D, SystemMath.Min(1D, double.Parse($"l:0.##")))
;
/// <summary>
/// Converts HSL to RGB, with a specified output Alpha.
/// Arguments are limited to the defined range:
/// does not raise exceptions.
/// </summary>
/// <param name="h">Hue, must be in [0, 360].</param>
/// <param name="s">Saturation, must be in [0, 1].</param>
/// <param name="l">Luminance, must be in [0, 1].</param>
/// <param name="a">Output Alpha, must be in [0, 255].</param>
public static Color HsLtoRgb(double h, double s, double l, int a = 255)
h = SystemMath.Max(0D, SystemMath.Min(360D, h));
s = SystemMath.Max(0D, SystemMath.Min(1D, s));
l = SystemMath.Max(0D, SystemMath.Min(1D, l));
a = SystemMath.Max(0, SystemMath.Min(255, a));
// achromatic argb (gray scale)
if (SystemMath.Abs(s) < SimpleColorTransforms.tolerance)
return Color.FromArgb(
a,
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"l * 255D:0.00")))),
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"l * 255D:0.00")))),
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"l * 255D:0.00")))));
double q = l < .5D
? l * (1D + s)
: (l + s) - (l * s);
double p = (2D * l) - q;
double hk = h / 360D;
double[] T = new double[3];
T[0] = hk + (1D / 3D); // Tr
T[1] = hk; // Tb
T[2] = hk - (1D / 3D); // Tg
for (int i = 0; i < 3; i++)
if (T[i] < 0D)
T[i] += 1D;
if (T[i] > 1D)
T[i] -= 1D;
if ((T[i] * 6D) < 1D)
T[i] = p + ((q - p) * 6D * T[i]);
else if ((T[i] * 2D) < 1)
T[i] = q;
else if ((T[i] * 3D) < 2)
T[i] = p + ((q - p) * ((2D / 3D) - T[i]) * 6D);
else
T[i] = p;
return Color.FromArgb(
a,
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"T[0] * 255D:0.00")))),
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"T[1] * 255D:0.00")))),
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"T[2] * 255D:0.00")))));
/// <summary>
/// Converts RGB to HSB. Alpha is ignored.
/// Output is: H: [0, 360], S: [0, 1], B: [0, 1] .
/// </summary>
/// <param name="color">The color to convert.</param>
public static double[] RgBtoHsb(Color color)
// normalize red, green and blue values
double r = color.R / 255D;
double g = color.G / 255D;
double b = color.B / 255D;
// conversion start
double max = SystemMath.Max(r, SystemMath.Max(g, b));
double min = SystemMath.Min(r, SystemMath.Min(g, b));
double h = 0D;
if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
&& (g >= b))
h = (60D * (g - b)) / (max - min);
else if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
&& (g < b))
h = ((60D * (g - b)) / (max - min)) + 360D;
else if (SystemMath.Abs(max - g) < SimpleColorTransforms.tolerance)
h = ((60D * (b - r)) / (max - min)) + 120D;
else if (SystemMath.Abs(max - b) < SimpleColorTransforms.tolerance)
h = ((60D * (r - g)) / (max - min)) + 240D;
double s = SystemMath.Abs(max) < SimpleColorTransforms.tolerance
? 0D
: 1D - (min / max);
return new[]
SystemMath.Max(0D, SystemMath.Min(360D, h)),
SystemMath.Max(0D, SystemMath.Min(1D, s)),
SystemMath.Max(0D, SystemMath.Min(1D, max))
;
/// <summary>
/// Converts HSB to RGB, with a specified output Alpha.
/// Arguments are limited to the defined range:
/// does not raise exceptions.
/// </summary>
/// <param name="h">Hue, must be in [0, 360].</param>
/// <param name="s">Saturation, must be in [0, 1].</param>
/// <param name="b">Brightness, must be in [0, 1].</param>
/// <param name="a">Output Alpha, must be in [0, 255].</param>
public static Color HsBtoRgb(double h, double s, double b, int a = 255)
h = SystemMath.Max(0D, SystemMath.Min(360D, h));
s = SystemMath.Max(0D, SystemMath.Min(1D, s));
b = SystemMath.Max(0D, SystemMath.Min(1D, b));
a = SystemMath.Max(0, SystemMath.Min(255, a));
double r = 0D;
double g = 0D;
double bl = 0D;
if (SystemMath.Abs(s) < SimpleColorTransforms.tolerance)
r = g = bl = b;
else
// the argb wheel consists of 6 sectors. Figure out which sector
// you're in.
double sectorPos = h / 60D;
int sectorNumber = (int)SystemMath.Floor(sectorPos);
// get the fractional part of the sector
double fractionalSector = sectorPos - sectorNumber;
// calculate values for the three axes of the argb.
double p = b * (1D - s);
double q = b * (1D - (s * fractionalSector));
double t = b * (1D - (s * (1D - fractionalSector)));
// assign the fractional colors to r, g, and b based on the sector
// the angle is in.
switch (sectorNumber)
case 0 :
r = b;
g = t;
bl = p;
break;
case 1 :
r = q;
g = b;
bl = p;
break;
case 2 :
r = p;
g = b;
bl = t;
break;
case 3 :
r = p;
g = q;
bl = b;
break;
case 4 :
r = t;
g = p;
bl = b;
break;
case 5 :
r = b;
g = p;
bl = q;
break;
return Color.FromArgb(
a,
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"r * 255D:0.00")))),
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"g * 255D:0.00")))),
SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"bl * 250D:0.00")))));
/// <summary>
/// Multiplies the Color's Luminance or Brightness by the argument;
/// and optionally specifies the output Alpha.
/// </summary>
/// <param name="color">The color to transform.</param>
/// <param name="colorTransformMode">Transform mode.</param>
/// <param name="brightnessTransform">The transformation multiplier.</param>
/// <param name="outputAlpha">Can optionally specify the Alpha to directly
/// set on the output. If null, then the input <paramref name="color"/>
/// Alpha is used.</param>
public static Color TransformBrightness(
Color color,
ColorTransformMode colorTransformMode,
double brightnessTransform,
byte? outputAlpha = null)
double[] hsl = colorTransformMode == ColorTransformMode.Hsl
? SimpleColorTransforms.RgBtoHsl(color)
: SimpleColorTransforms.RgBtoHsb(color);
if ((SystemMath.Abs(hsl[2]) < SimpleColorTransforms.tolerance)
&& (brightnessTransform > 1D))
hsl[2] = brightnessTransform - 1D;
else
hsl[2] *= brightnessTransform;
return colorTransformMode == ColorTransformMode.Hsl
? SimpleColorTransforms.HsLtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A)
: SimpleColorTransforms.HsBtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A);
/// <summary>
/// Multiplies the Color's Saturation, and Luminance or Brightness by the argument;
/// and optionally specifies the output Alpha.
/// </summary>
/// <param name="color">The color to transform.</param>
/// <param name="colorTransformMode">Transform mode.</param>
/// <param name="saturationTransform">The transformation multiplier.</param>
/// <param name="brightnessTransform">The transformation multiplier.</param>
/// <param name="outputAlpha">Can optionally specify the Alpha to directly
/// set on the output. If null, then the input <paramref name="color"/>
/// Alpha is used.</param>
public static Color TransformSaturationAndBrightness(
Color color,
ColorTransformMode colorTransformMode,
double saturationTransform,
double brightnessTransform,
byte? outputAlpha = null)
double[] hsl = colorTransformMode == ColorTransformMode.Hsl
? SimpleColorTransforms.RgBtoHsl(color)
: SimpleColorTransforms.RgBtoHsb(color);
if ((SystemMath.Abs(hsl[1]) < SimpleColorTransforms.tolerance)
&& (saturationTransform > 1D))
hsl[1] = saturationTransform - 1D;
else
hsl[1] *= saturationTransform;
if ((SystemMath.Abs(hsl[2]) < SimpleColorTransforms.tolerance)
&& (brightnessTransform > 1D))
hsl[2] = brightnessTransform - 1D;
else
hsl[2] *= brightnessTransform;
return colorTransformMode == ColorTransformMode.Hsl
? SimpleColorTransforms.HsLtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A)
: SimpleColorTransforms.HsBtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A);
/// <summary>
/// Creates a new Color by combining R, G, and B from each Color, scaled by the Color's Alpha.
/// The R, G, B of each Color is scaled by the Color's Alpha. The R, G, B of both results is
/// then added together and divided by 2. The valuea are limited to [0, 255].
/// The Alpha of the output Color is specified; and is also limited to [0, 255]
/// (does not raise exceptions).
/// </summary>
/// <param name="color1">Combined by scaling RGB by the A.</param>
/// <param name="color2">Combined by scaling RGB by the A.</param>
/// <param name="outputAlpha">The Alpha of the output Color.</param>
public static Color AlphaCombine(Color color1, Color color2, byte outputAlpha)
double a1 = color1.A / 255D;
double a2 = color2.A / 255D;
return Color.FromArgb(
outputAlpha,
(byte)SystemMath.Max(0D, SystemMath.Min(255D, ((color1.R * a1) + (color2.R * a2)) * .5D)),
(byte)SystemMath.Max(0D, SystemMath.Min(255D, ((color1.G * a1) + (color2.G * a2)) * .5D)),
(byte)SystemMath.Max(0D, SystemMath.Min(255D, ((color1.B * a1) + (color2.B * a2)) * .5D)));
【讨论】:
【参考方案2】:将以下行添加到代码文件中的 "using" 命名空间:
using System.Runtime.InteropServices
并使用 P/Invoke,添加以下函数:
[DllImport("shlwapi.dll")]
public static extern int ColorHLSToRGB(int H, int L, int S);
在你的项目中你想要的任何类。
如果您将色调、亮度和饱和度中的颜色设为整数,则将这些值提供给该函数,所需的 RGB 颜色将以整数形式返回。 然后使用 ColorTranslator.FromWin32 将返回的 rgb 颜色作为整数转换为 System.Drawing.Color 结构。
另一种方法是使用 Color.FromArgb(Int32) 重载方法,根据该整数创建您想要的 System.Drawing.Color 结构的颜色。
另一种方法是创建一个ColorConverter类的实例,然后调用ConvertFrom方法,并给它返回的RGB颜色整数ColorHLSToRGB 函数,或使用相同的第一个参数调用 ConvertTo 方法,但在第二个参数中输入 typeof(System.Drawing.Color)
。然后将返回的对象转换为 System.Drawing.Color 结构类型。
结果正是您所寻求和期望的!
您还可以尝试其他算法或方法,您可以在互联网上搜索、查找和学习,了解如何将 RGB 颜色作为整数转换为 System.Drawing.Color 结构或如何将 HLS 值作为三个整数转换为 System .Drawing.Color 结构!
【讨论】:
有几点需要注意:H
、L
和 S
从 0 到 240。请记住,亮度 0 表示黑色,240 表示白色,所以如果你想要一个纯色(比如#F00
)你需要将L
设置为120。另外值得注意的是:如果你要使用Color.FromArgb(Int32)
重载,颜色的alpha值为0,这意味着它将是透明的。 ColorTranslator.FromWin32()
没有这个问题。以上是关于从 HSL 值创建 C# 颜色的主要内容,如果未能解决你的问题,请参考以下文章