以编程方式减轻颜色


72

动机

我想找到一种方法来采用任意一种颜色并将其变浅一些阴影,以便可以以编程方式创建一个从一种颜色到较浅版本的渐变。渐变将用作UI中的背景。

可能性1

显然,我可以将RGB值分开并分别增加一定数量。这真的是我想要的吗?

可能性2

我的第二个想法是将RGB转换为HSV / HSB / HSL(色相,饱和度,值/亮度/亮度),稍微提高亮度,降低饱和度,然后再将其转换回RGB。总体上会达到预期的效果吗?


40
编程语言无关紧要。
David Arno

如果背景为白色,则修改alpha /透明度通常可以很好地用作“变亮”颜色的技巧。
阿斯特列佐夫

Answers:


38

我会选择第二种选择。一般来说,RGB空间并不是真正适合进行颜色处理(创建从一种颜色到另一种颜色的过渡,使颜色变亮/变暗等)。以下是我找到的两个站点,可以通过快速搜索将RGB转换为HSL:


转换源代码的良好替代链接:bobpowell.net/RGBHSB.aspx
Curtis Yallop


58

正如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值执行了钳位。结果相似,但是需要对每个像素进行颜色空间转换。

HLS(顶部)和HSV(底部)的亮度梯度


对此表示支持之后,我意识到这是一种过大的杀伤力。您需要做的就是以一定的不透明度将Alpha与白色混合(即,将颜色分量以255的相同比例插入)。
安迪2015年

@Andy看一下示例。左侧的前两个方框中没有白色,它们已完全饱和。如果我从较暗的颜色开始,情况会更好。
Mark Ransom

嗯,我知道了,所以您的代码具有与HLS颜色空间相同的线性不连续性。我可以看到它将如何产生更丰富的渐变。由于我只是想淡化悬停高光的颜色而不是进行渐变,所以我期待一种更简单的方法
Andy

1
@Andy还有一个答案,建议用白色进行线性插值,我相信这正是您所追求的。
Mark Ransom

是的,我要自己添加一个答案,直到我看到别人做了。干杯!
安迪

25

在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) );
}

我已经在整个地方使用了。


非常方便,简单。现在在VB.NET应用程序中使用它。
Kezzer


如果您正在使用using System.Windows.Media.Color;,则不应该强制转换为byte吗?
戴维

11

System.Windows.Forms命名空间中的ControlPaint类具有静态方法Light和Dark:

public static Color Dark(Color baseColor, float percOfDarkDark);

这些方法使用HLSColor的私有实现。我希望这个结构是公开的,并且在System.Drawing中。

另外,您可以在Color结构上使用GetHue,GetSaturation,GetBrightness获取HSB组件。不幸的是,我没有找到反向转换。


8

将其转换为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;
}

在大多数情况下,仅需要开始和结束颜色,因为选择的图形库可能具有内置的渐变功能。
Max Schmeling

通过使用整数除法,您无需舍入(底数)。聪明。
Stefan Steiger,2015年

6

为了获得给定颜色的浅色或深色版本,您应该修改其亮度。即使不将颜色转换为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);

如果您需要更多详细信息,请阅读我的博客上的完整故事


+1,尼克斯方法,正确的方法。但是,您仍然可以像Adam Adam Rosenfield一样通过将校正因子分为步长和索引来跳过对浮点数的转换,这也提供了两个可以理解的值,而不是0.0和1.0之间的一个抽象数。例如,您的校正因子是Rosenfield的i / 15,同时假设白色为目标颜色。
Stefan Steiger,2015年

3

转换为HS(LVB),增加亮度然后转换回RGB是唯一一种可靠地使颜色变浅而不影响色相和饱和度值的方法(即,仅使颜色变浅而不用其他任何方式更改颜色)。



3

我使用安德鲁(Andrew)的答案和马克(Mark)的答案来做到这一点(从1/2013开始,ff没有范围输入)。

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) }
}

在此处输入图片说明


2

我已经做过这两种方式-情形2可以使您获得更好的结果。

您为可能性1构造的任何简单算法都可能仅对有限的起始饱和度范围有效。

如果(1)您可以限制使用的颜色和亮度,并且(2)您在渲染中执行大量计算,则可能需要考虑位置1。

生成UI的背景并不需要太多的阴影计算,因此我建议使用Poss 2。

-Al


1

如果要产生渐变淡入,我建议进行以下优化:而不是对每种颜色进行RGB-> HSB-> RGB,您应该只计算目标颜色。知道目标RGB后,您就可以简单地计算RGB空间中的中间值,而无需来回转换。是否计算使用某种曲线的线性过渡取决于您。



1

假设您将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,用回零(r, g, b)和1返回(dest.r, dest.g, dest.b)


我认为这应该是最佳答案。同样,为了变暗,只需将alpha混合为黑色(即,将分量乘以amount < 1)。
安迪

1

直到它成为与原始问题相关的问题后,我才找到这个问题。

但是,请使用这些出色答案中的洞察力。为此,我拼凑了一个很好的两层函数:

以编程方式使十六进制颜色(或rgb和混合颜色)变亮或变暗

它是方法1的一种版本。但是考虑到过饱和。就像基思在上面的回答中说的那样;使用Lerp似乎解决了Mark提到的相同问题,但没有重新分配。的结果shadeColor2应该更接近于使用HSL正确地执行此操作,但没有开销。


1

晚会晚了一点,但是如果您使用javascriptnodejs,则可以使用tinycolor库,并以所需的方式操作颜色:

tinycolor("red").lighten().desaturate().toHexString() // "#f53d3d" 

0

我本来应该先尝试#1,但是#2听起来不错。尝试自己动手做,看看您是否对结果满意,这听起来可能要花10分钟才能完成测试。


0

从技术上讲,我都不认为这是正确的,但我相信您想要选项2的变体。问题是采用RGB 990000并“变亮”它实际上只会添加到红色通道(“值”,“亮度”,“亮度”),直到达到FF。之后(红色常亮),它将降低饱和度,一直到纯白色。

转换变得很烦人,尤其是因为您无法直接往返于RGB和Lab,但我认为您确实想将色度和亮度值分开,只修改亮度以真正实现您想要的。




0

这是在Python中减轻RGB颜色的示例:

def lighten(hex, amount):
    """ Lighten an RGB color by an amount (between 0 and 1),

    e.g. 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))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.