以编程方式减轻颜色
动机
我想找到一种方法来采用任意一种颜色并将其变浅一些阴影,以便可以以编程方式创建一个从一种颜色到较浅版本的不错的渐变。渐变将用作UI中的背景。
可能性1
显然,我可以将RGB值分开并分别增加一定数量。这真的是我想要的吗?
可能性2
我的第二个想法是将RGB转换为HSV / HSB / HSL(色调,饱和度,值/亮度/亮度),稍微提高亮度,降低饱和度,然后再将其转换回RGB。总体上会达到预期的效果吗?
解决方案
我会选择第二种选择。通常来说,RGB空间并不是真正适合进行颜色处理(创建从一种颜色到另一种颜色的过渡,使颜色变亮/变暗等)。以下是我找到的两个站点,可以通过快速搜索将RGB转换为HSL:
- 摘自"计算机图形学基础"
- C#中的某些源代码-应该易于适应其他编程语言。
我本来应该先尝试#1,但是#2听起来还不错。尝试自己动手做,看看我们是否对结果满意,这听起来可能要花10分钟才能完成测试。
先前曾问过一个非常相似的问题,并提供了有用的答案:
如何确定给定颜色的较暗或者较浅颜色?
简短的答案:如果只需要"足够好",则将RGB值乘以一个常数,如果需要精度,则将其转换为HSV。
在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) ); }
我在整个地方都用过这个。
转换为HS(LVB),增加亮度然后转换回RGB是可靠地淡化颜色而不影响色相和饱和度值(即仅淡化颜色而不以其他任何方式更改颜色)的唯一方法。
将其转换为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; }
我已经做过这两种方式-在Possibility 2中,我们可以获得更好的结果。
我们为可能性1构造的任何简单算法都可能仅对有限的起始饱和度范围有效。
如果(1)我们可以限制所使用的颜色和亮度,并且(2)我们在渲染中执行大量计算,则可能需要考虑位置1.
生成UI的背景并不需要太多的阴影计算,因此我建议使用Poss 2.
-Al
从技术上讲,我认为这都不对,但是我相信我们想要选项2的变体。问题在于采用RGB 990000并"变亮"它实际上只会添加到红色通道("值","亮度","亮度"),直到达到FF。在那之后(纯红色),它将降低饱和度,一直到纯白色。
转换变得很烦人,尤其是因为我们无法直接往返于RGB和Lab,但我认为我们确实想将色度和亮度值分开,而只是修改亮度以真正实现所需的功能。
正如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)
我创建了一个以RGB值(224,128,0)开头的渐变,然后将其乘以1.0、1.1、1.2等,直到2.0。上半部分是使用clamp_rgb
的结果,下半部分是使用redistribute_rgb
的结果。我认为很容易看出,重新分配溢出可以带来更好的结果,而不必离开RGB颜色空间。
为了进行比较,这是HLS和HSV颜色空间中的渐变,与Python的colorsys
模块所实现的相同。仅修改了" L"分量,并对生成的RGB值执行了钳位。结果相似,但需要对每个像素进行颜色空间转换。
我们将在颜色工具ruby库中找到在颜色空间之间转换的代码
System.Windows.Forms命名空间中的ControlPaint类具有静态方法Light和Dark:
public static Color Dark(Color baseColor, float percOfDarkDark);
这些方法使用HLSColor的私有实现。我希望这个结构是公开的,并且在System.Drawing中。
或者,可以在Color结构上使用GetHue,GetSaturation,GetBrightness获取HSB组件。不幸的是,我没有找到反向转换。
如果要产生渐变淡入,我建议进行以下优化:而不是对每种颜色进行RGB-> HSB-> RGB,我们应该只计算目标颜色。知道目标RGB后,我们可以简单地计算RGB空间中的中间值,而无需来回转换。是否计算使用某种曲线的线性过渡取决于我们。
我有一篇博客文章,展示了如何在Delphi中执行此操作。它非常简单,因为函数ColorRGBToHSL和ColorHLSToRGB是标准库的一部分。
方法1:将RGB转换为HSL,调整HSL,再转换回RGB。
方法2:Lerp RGB颜色值http://en.wikipedia.org/wiki/Lerp_(计算)
有关方法2的实现,请参见我对类似问题的回答。