如何确定给定颜色的较暗或较浅变体?

44
系统或用户提供的任何色调的源颜色,我想要一个简单的算法来计算出所选颜色的较浅或较深的变体。类似于在Windows Live Messenger上用于设计用户界面的效果。
使用的编程语言是C#和.NET 3.5。
回答评论:颜色格式为(Alpha)RGB,值为字节或浮点数。
标记答案:对于我的应用场景(一些简单的UI效果),我选择接受最简单的答案。然而,我也给了更复杂和准确的答案赞成票。未来进行更高级的颜色操作并发现这个线程的任何人都应该查看这些答案。谢谢 Stack Overflow。 :)

1
仅供补充,目前用于颜色转换和插值(例如创建感知均匀的颜色映射等)的最先进的色彩空间是CIELab。 - heltonbiker
13个回答

53

在XNA中有一个静态方法Color.Lerp了解更多),它可以计算两种颜色之间的差异。

Lerp是介于两个浮点数之间的数学运算,通过这个运算可以按比例改变第一个浮点数的值。

下面是一个扩展方法,用于将它应用到浮点数中:

public static float Lerp( this float start, float end, float amount)
{
    float difference = end - start;
    float adjusted = difference * amount;
    return start + adjusted;
}

那么使用RGB对两种颜色进行简单的线性插值操作如下:

public static Color Lerp(this Color colour, Color to, float amount)
{
    // start colours as lerp-able floats
    float sr = colour.R, sg = colour.G, sb = colour.B;

    // end colours as lerp-able floats
    float er = to.R, eg = to.G, eb = to.B;

    // lerp the colours to get the difference
    byte r = (byte) sr.Lerp(er, amount),
         g = (byte) sg.Lerp(eg, amount),
         b = (byte) sb.Lerp(eb, amount);

    // return the new colour
    return Color.FromArgb(r, g, b);
}

应用此方法的示例可能如下:

// make red 50% lighter:
Color.Red.Lerp( Color.White, 0.5f );

// make red 75% darker:
Color.Red.Lerp( Color.Black, 0.75f );

// make white 10% bluer:
Color.White.Lerp( Color.Blue, 0.1f );

10
我现在已经习惯了,在编译的C#中这并不那么糟糕,但由于拼写"centre"或"colour"错误,我曾花费很长时间试图找出HTML/CSS/JS代码的问题:S - Keith
示例的语法让我困惑了,但现在我明白了...只需重新排列一下,我想。 - ycomp
返回语句需要 alpha 值,例如:return Color.FromArgb(colour.A, r, g, b); 或者 RGB 版本:return Color.FromRgb(r, g, b); - Lennart

28

将RGB值乘以您要修改的级别量即可。 如果其中一种颜色已经达到最大值,那么您无法使其更加明亮(至少使用HSV算法是这样的)。

这样做可以用更少的计算得到完全相同的结果,而不必切换到HSV然后修改V。 只要您不想开始失去饱和度,这与切换到HSL然后修改L得到相同的结果。


1
是的,即使您无法使单个颜色通道变亮,也有可能使颜色更亮。失去饱和度是不可避免的副作用。请参见https://dev59.com/CnVC5IYBdhLWcg3w7V73#141943。 - Mark Ransom
如果颜色是黑色,将RGB值相乘不会产生任何效果。 - Dylanthepiguy

21

HSV(色相/饱和度/明度)也称为HSL(色相/饱和度/亮度),它只是一种不同的颜色表示方法。

使用此表示方法可以更轻松地调整亮度。因此,将RGB转换为HSV,增加“V”,然后再转换回RGB。

以下是一些用于转换的C代码:

void RGBToHSV(unsigned char cr, unsigned char cg, unsigned char cb,double *ph,double *ps,double *pv)
{
double r,g,b;
double max, min, delta;

/* convert RGB to [0,1] */

r = (double)cr/255.0f;
g = (double)cg/255.0f;
b = (double)cb/255.0f;

max = MAXx(r,(MAXx(g,b)));
min = MINx(r,(MINx(g,b)));

pv[0] = max;

/* Calculate saturation */

if (max != 0.0)
    ps[0] = (max-min)/max;
else
    ps[0] = 0.0; 

if (ps[0] == 0.0)
{
    ph[0] = 0.0f;   //UNDEFINED;
    return;
}
/* chromatic case: Saturation is not 0, so determine hue */
delta = max-min;

if (r==max)
{
    ph[0] = (g-b)/delta;
}
else if (g==max)
{
    ph[0] = 2.0 + (b-r)/delta;
}
else if (b==max)
{
    ph[0] = 4.0 + (r-g)/delta;
}
ph[0] = ph[0] * 60.0;
if (ph[0] < 0.0)
    ph[0] += 360.0;
}

void HSVToRGB(double h,double s,double v,unsigned char *pr,unsigned char *pg,unsigned char *pb)
{
int i;
double f, p, q, t;
double r,g,b;

if( s == 0 )
{
    // achromatic (grey)
    r = g = b = v;
}
else
{
    h /= 60;            // sector 0 to 5
    i = (int)floor( h );
    f = h - i;          // factorial part of h
    p = v * ( 1 - s );
    q = v * ( 1 - s * f );
    t = v * ( 1 - s * ( 1 - f ) );
    switch( i )
    {
    case 0:
        r = v;
        g = t;
        b = p;
    break;
    case 1:
        r = q;
        g = v;
        b = p;
    break;
    case 2:
        r = p;
        g = v;
        b = t;
    break;
    case 3:
        r = p;
        g = q;
        b = v;
    break;
    case 4:
        r = t;
        g = p;
        b = v;
    break;
    default:        // case 5:
        r = v;
        g = p;
        b = q;
    break;
    }
}
r*=255;
g*=255;
b*=255;

pr[0]=(unsigned char)r;
pg[0]=(unsigned char)g;
pb[0]=(unsigned char)b;
}

6
HSV和HSL是不同的概念。HSV并非也叫做HSL。请参考此链接了解详情:http://en.wikipedia.org/wiki/HSL_and_HSV。 - Roman Starkov
但是HSV有时被称为HSB(亮度)。这仍然与HSL不同,正如@romkyns所说。 - Mark Embling
@KPexEA你写道:“所以将RGB转换为HSV,提高'V'的亮度,然后再转换回RGB。”。我该如何*提高“V”部分的亮度?我需要生成相同颜色的n个不同阴影。这些阴影应该逐渐变亮。 - Geek
@Geek 将颜色从RGB转换为HSV,然后丢弃V。然后使用HS值调用HSVtoRGB,并随着V的增加迭代生成不同的RGB阴影。或者,如果您想以初始值开始并使它们变得更亮,只需为每次迭代的每个V添加一小部分,而不是从0开始。 - KPexEA
@KPexEA 如果初始值为 V=0.25,且我需要从给定颜色开始使用 4 种较浅的色调,则 V 的值将为 0.25、0.5、0.75 和 1?这些数字是否总是需要遵循*等差数列(AP)*? - Geek

14

Rich Newman在他的博客上讨论了HSL颜色,并针对.NET System.Drawing.Color进行了探讨,甚至提供了一个HSLColor类来为您完成所有工作。将您的System.Drawing.Color转换为HSLColor,在亮度方面添加/减去值,并将其转换回System.Drawing.Color以供在您的应用程序中使用。


10

你可以将颜色转换为HSL颜色空间,在那里操作并转换回您选择的颜色空间(最可能是RGB)。

较浅的颜色具有更高的L值,而较暗的颜色则具有较低的L值。

以下是相关的内容和所有方程式:

http://en.wikipedia.org/wiki/HSL_color_space

另一种方法是简单地将您的颜色与白色或黑色插值。这也会使颜色变得减淡一些,但计算成本较低。


这些计算通常很便宜,但需要编写的代码量对程序员来说是一种负担。 - heltonbiker

9

我曾经使用过System.Windows.Forms中的ControlPaint.Dark().Light()


3
辉煌。为什么它不是“颜色”命名空间的一部分? - itsho
1
注意,方法的第二个参数'percOfLightLight'应该是一个范围在0到1之间的浮点数。虽然可以接受其他值,但行为可能不是您想要的。获取较浅颜色的示例代码如下:Color lighterColor = ControlPaint.Light(originalColor, 1.0f); - Jinlye

6
我猜您正在使用RGB(0到255的字节值),因为这在各个地方都非常常见。
为了使颜色更亮,可以将RGB值与白色的RGB平均。或者,为了对亮度有更多控制,可以按一定比例混合它们。假设 f 的取值范围为 0.0 到 1.0,则有以下公式:
Rnew = (1-f)*R + f*255
Gnew = (1-f)*G + f*255
Bnew = (1-f)*B + f*255

如果要使用较暗的色调,请使用黑色的RGB值——因为它全是零,数学计算更加容易。

我省略了一些细节,比如将结果转换回字节,这可能是你想要做的。


太棒了!这对我非常有效。 - Norbert S

3
如果您正在使用RGB颜色,我会将此颜色参数转换为HSL(色调,饱和度,亮度),修改亮度参数,然后再转换回RGB。在谷歌上搜索,您会发现很多关于如何进行这些颜色表示转换的代码示例(从RGB到HSL以及相反)。
这是我快速找到的内容: http://bytes.com/forum/thread250450.html

1
假设您的颜色是RGB格式,首先将其转换为HSV(色相,饱和度,明度)颜色空间。然后增加/减少亮度,以产生更浅/更深的颜色阴影。最后将其转换回RGB格式。

0

这个网站指出,你可以在BCL C# System.Windows.Forms命名空间中使用ControlPaint类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接