以编程方式减轻颜色
动机
我想find一个方法来采取任意的颜色,并减轻一些阴影,所以我可以编程方式创build一个从一个颜色到一个更轻的版本的一个很好的渐变。 渐变将用作UI中的背景。
可能性1
很明显,我可以分开RGB值,并逐个增加一定的数量。 这实际上是我想要的吗?
可能性2
我的第二个想法是将RGB转换为HSV / HSB / HSL(色相,饱和度,数值/亮度/亮度),稍微提高亮度,稍微降低饱和度,然后将其转换回RGB。 这会有一般的预期效果吗?
我会去第二个选项。 一般来说,RGB空间对于颜色操作(创build从一种颜色到另一种颜色的转换,使颜色变亮/变暗等)并不是很好。 以下是我find的两个站点,可以快速search将HSL转换为/从RGB转换为HSL:
- 从“计算机graphics学基础”
- 在C#中的一些源代码 – 应该很容易适应其他编程语言。
正如Wedge所说 ,你想要使事情变得更加明亮,但是只有一种颜色变得饱和(即击中255或更大)。 在这一点上,你可以把值限制到255,但是你会轻微地改变色调。 要保持色调,你想要保持(中低档)/(最高 – 最低)的比例。
这是Python中的两个函数。 第一个实现了简单的方法,只要将RGB值限制在255即可。 第二个重新分配多余的值,保持色调完好。
def clamp_rgb(r, g, b): return min(255, int(r)), min(255, int(g)), min(255, int(b)) def redistribute_rgb(r, g, b): threshold = 255.999 m = max(r, g, b) if m <= threshold: return int(r), int(g), int(b) total = r + g + b if total >= 3 * threshold: return int(threshold), int(threshold), int(threshold) x = (3 * threshold - total) / (3 * m - total) gray = threshold - x * m return int(gray + x * r), int(gray + x * g), int(gray + x * b)
我创build了一个从RGB值(224,128,0)开始的渐变,并将其乘以1.0,1.1,1.2等直到2.0。 上半部分是使用clamp_rgb
的结果,下半部分是使用redistribute_rgb
的结果。 我认为很容易看到,重新分配溢出会得到更好的结果,而不必离开RGB色彩空间。
为了进行比较,这里是在Python的colorsys
模块中实现的HLS和HSV颜色空间中相同的渐变。 只有L
组件被修改,并且对所得到的RGB值执行了钳位。 结果是相似的,但需要每个像素的色彩空间转换。
在C#中:
public static Color Lighten(Color inColor, double inAmount) { return Color.FromArgb( inColor.A, (int) Math.Min(255, inColor.R + 255 * inAmount), (int) Math.Min(255, inColor.G + 255 * inAmount), (int) Math.Min(255, inColor.B + 255 * inAmount) ); }
我已经使用了这个地方。
System.Windows.Forms命名空间中的ControlPaint类具有静态方法Light and Dark:
public static Color Dark(Color baseColor, float percOfDarkDark);
这些方法使用HLSColor的私有实现。 我希望这个结构是公开的,并在System.Drawing。
或者,您可以在Color结构上使用GetHue,GetSaturation,GetBrightness来获取HSB组件。 不幸的是,我没有find反向转换。
将其转换为RGB,并在原始颜色和目标颜色(通常为白色)之间进行线性插值。 所以,如果你想要两种颜色之间的16个阴影,你可以这样做:
for(i = 0; i < 16; i++) { colors[i].R = start.R + (i * (end.R - start.R)) / 15; colors[i].G = start.G + (i * (end.G - start.G)) / 15; colors[i].B = start.B + (i * (end.B - start.B)) / 15; }
为了获得给定颜色的较亮或较暗的版本,您应该修改其亮度。 即使不将颜色转换为HSL或HSB颜色,也可以轻松完成此操作。 例如,要使颜色变浅,可以使用以下代码:
float correctionFactor = 0.5f; float red = (255 - color.R) * correctionFactor + color.R; float green = (255 - color.G) * correctionFactor + color.G; float blue = (255 - color.B) * correctionFactor + color.B; Color lighterColor = Color.FromArgb(color.A, (int)red, (int)green, (int)blue);
如果您需要更多的细节,请阅读我博客上的全文 。
一个非常类似的问题,有用的答案,先前被问到: 我如何确定一个给定颜色的颜色变暗或变暗?
简短的回答:如果您只需要“足够好”,将RGB值乘以一个常数,如果您需要精确度,则转换为HSV。
转换为HS(LVB),增加亮度,然后转换回RGB是可靠地减轻颜色而不影响色相和饱和度值的唯一方法(即只在不改变颜色的情况下才减轻颜色)。
我已经完成了这两种方式 – 你可能得到更好的结果与可能性2。
你为Possibility 1构build的任何简单的algorithm可能只适用于有限范围的起始饱和。
如果(1)可以限制使用的颜色和亮度,以及(2)在渲染过程中执行计算,则需要查看Poss 1。
生成一个UI的背景将不需要太多的阴影计算,所以我build议Poss 2。
-Al。
我用安德鲁的答案和马克的答案,使(从2013年1月没有范围inputff)。
function calcLightness(l, r, g, b) { var tmp_r = r; var tmp_g = g; var tmp_b = b; tmp_r = (255 - r) * l + r; tmp_g = (255 - g) * l + g; tmp_b = (255 - b) * l + b; if (tmp_r > 255 || tmp_g > 255 || tmp_b > 255) return { r: r, g: g, b: b }; else return { r:parseInt(tmp_r), g:parseInt(tmp_g), b:parseInt(tmp_b) } }
如果你想产生一个渐变淡出,我会build议以下优化:而不是做每个单独的颜色的RGB-> HSB-> RGB,你应该只计算目标颜色。 一旦知道了目标RGB,就可以简单地计算RGB空间中的中间值,而不必前后转换。 无论你计算使用某种曲线的线性转换是由你决定的。
方法1:将RGB转换为HSL,调整HSL,转换回RGB。
方法2: Lerp RGB颜色值 – http://en.wikipedia.org/wiki/Lerp_(computing);
看到我对这个类似的问题的方法2的C#实现的答案 。
假装你alpha混合成白色:
oneMinus = 1.0 - amount r = amount + oneMinus * r g = amount + oneMinus * g b = amount + oneMinus * b
amount
从0到1,0返回原来的颜色,1返回白色。
如果您想要显示禁用的内容,则可能需要混合背景颜色:
oneMinus = 1.0 - amount r = amount * dest_r + oneMinus * r g = amount * dest_g + oneMinus * g b = amount * dest_b + oneMinus * b
其中(dest_r, dest_g, dest_b)
是要混合的颜色, amount
从0到1,返回值为0 (r, g, b)
,返回(dest.r, dest.g, dest.b)
1 (dest.r, dest.g, dest.b)
直到它成为我原来的问题的相关问题之后,才发现这个问题。
然而,从这些伟大的答案使用洞察力。 我拼凑了一个很好的双线function:
以编程方式增亮或减淡hex颜色(或rgb,并混合颜色)
它的方法1的一个版本。但过饱和考虑到。 就像基思在上面的回答中所说的那样; 使用Lerp似乎解决马克提到的同样的问题,但没有重新分配。 shadeColor2
的结果应该更接近HSL的正确方式,但没有开销。
我会先尝试数字1,但#2听起来不错。 试着自己去做,看看你对结果是否满意,听起来好像需要10分钟才能完成一个testing。
从技术上讲,我不认为这是正确的,但我相信你想要一个选项#2的变种。 问题是,采取RGB 990000和“减轻”它真的只是添加到红色通道(价值,亮度,亮度),直到你达到FF。 之后(稳定的红色),它会减less饱和度 ,一路走向稳定的白色。
转换变得烦人,特别是因为你不能直接进出RGB和Lab,但是我认为你真的想分离出色度和发光值,而只是修改发光量来达到你想要的效果。
你会发现在颜色工具ruby库中的颜色空间之间进行转换的代码
我有一个博客文章 ,展示如何在Delphi中做到这一点。 它非常简单,因为ColorRGBToHSL和ColorHLSToRGB函数是标准库的一部分。
这是一个在Python中减轻RGB颜色的例子:
def lighten(hex, amount): """ Lighten an RGB color by an amount (between 0 and 1), eg lighten('#4290e5', .5) = #C1FFFF """ hex = hex.replace('#','') red = min(255, int(hex[0:2], 16) + 255 * amount) green = min(255, int(hex[2:4], 16) + 255 * amount) blue = min(255, int(hex[4:6], 16) + 255 * amount) return "#%X%X%X" % (int(red), int(green), int(blue))