如何确定给定颜色的较暗或者较浅颜色?
给定系统或者用户的任何色调的源颜色,我想使用一种简单的算法来计算出所选颜色的较浅或者较深的变体。与Windows Live Messenger上用于设置用户界面样式的效果相似。
语言是Cwith .net 3.5.
回应评论:颜色格式为(Alpha)RGB。值以字节或者浮点数表示。
标记答案:就我使用的上下文(一些简单的UI效果)而言,我标记为接受的答案实际上是此上下文中最简单的。但是,我也放弃了对更复杂和准确的答案的投票。任何进行更高级的色彩操作并在将来找到该线程的人都应该检查一下。谢谢。 :)
解决方案
我们可以将颜色转换为HSL颜色空间,在此处进行处理,然后转换回我们选择的颜色空间(最有可能是RGB)
较浅的颜色具有较高的L值,较深的颜色具有较低的L值。
这是相关的东西和所有方程式:
http://en.wikipedia.org/wiki/HSL_color_space
另一种方法是简单地用白色或者黑色插值颜色。这也会使颜色略微降低饱和度,但是计算起来更便宜。
假设我们获得的颜色为RGB,首先将其转换为HSV(色相,饱和度,值)颜色空间。然后增加/减少该值以产生较浅/较深的颜色阴影。然后转换回RGB。
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; }
如果颜色是RGB格式(或者大概是CMYK),则可以使用相当粗略的方法来增加颜色的每个成分的值。例如,在HTML中,颜色表示为三个两位数的十六进制数字。 #ff0000将为我们提供明亮的红色,然后可以通过将G和B组件的值增加相同的数量来淡化,例如#ff5555(提供较浅的红色)。大概对于色相,饱和度和亮度(HSL)颜色,我们可以提高L分量,但是我不能肯定地说。我对这个色彩空间不太熟悉。
正如我所说,这种方法非常粗糙。从我对Live Messenger的记忆中,听起来好像我们正在尝试进行渐变,可以非常轻松地在Windows Presentation Foundation(WPF,.NET 3.0的一部分)中应用渐变。 WPF支持许多不同类型的渐变笔刷,包括线性和径向渐变。
我强烈推荐Adam Nathan的书《 Windows Presentation Foundation Unleashed》作为WPF的详尽介绍。
高温超导
如果我们使用的是RGB颜色,则可以将此颜色参数转换为HSL(色调,饱和度,亮度),修改亮度参数,然后转换回RGB。到处都是Google,我们会发现很多有关如何进行这些颜色表示转换(从RGB到HSL,反之亦然)的代码示例。
这是我很快发现的:
http://bytes.com/forum/thread250450.html
只需将RGB值乘以我们要修改级别的数量即可。如果其中一种颜色已经达到最大值,那么我们就无法使其更亮(无论如何都要使用HSV数学)。
这与使用HSV然后修改V所得到的结果完全相同,而数学运算要少得多。只要我们不想开始失去饱和度,就可以得到与使用HSL然后修改L相同的结果。
在HSL / HSV中更好地完成颜色的任何变化。
一个好的测试是在RGB空间和HSL空间中的两个等效值之间进行插值。 HSL空间的坡度看起来像是自然发展的过程。在RGB空间中,它通常看起来非常不自然。 HSL比RGB更好地映射到我们的视觉色彩空间感知。
我猜我们正在使用带字节值(0到255)的RGB,因为到处都是很常见的。
为获得更亮的效果,请用白色的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(全为零),使数学运算更容易。
我遗漏了一些细节,例如将结果转换回字节,这可能是我们想要的。
转换为HSV或者其他颜色空间的想法似乎不错,并且对于精确的颜色工作可能是必需的,但是对于一般目的,使用RGB工作的错误可能并不重要。同样,处理边界情况可能会很痛苦:RGB是一个立方体形状的空间,而HSV不是。如果使用字节值,则空间之间可以具有多对一和一对多的映射。取决于应用程序,这可能是问题,也可能不是问题。青年汽车
Rich Newman在他的博客中讨论了有关.NET System.Drawing.Color的HSL颜色,甚至提供了HSLColor类来为我们完成所有工作。将System.Drawing.Color转换为HSLColor,针对光度添加/减去值,然后转换回System.Drawing.Color以在应用中使用。
我在System.Windows.Forms中使用了ControlPaint.Dark()和.Light()。
该网站指出,可以在BCL CSystem.Windows.Forms命名空间中使用ControlPaint类。
在XNA中,有一种Color.Lerp静态方法将其作为两种颜色之间的差异进行处理。
" Lerp"是两个浮点数之间的数学运算,它会根据两个浮点数之间的差值来改变第一个浮点数的值。
这是对float
进行扩展的一种扩展方法:
public static float Lerp( this float start, float end, float amount) { float difference = end - start; float adjusted = difference * amount; return start + adjusted; }
因此,使用RGB在两种颜色之间进行简单的lerp操作将是:
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 );