以编程方式减轻颜色

时间:2008-09-26 20:43:36

标签: user-interface language-agnostic colors

动机

我想找到一种方法来拍摄任意颜色并使其变暗几种色调,这样我就可以编程方式创建从一种颜色到较轻版本的漂亮渐变。渐变将用作UI中的背景。

可能性1

显然,我可以将RGB值分开并逐个增加一定量。这实际上是我想要的吗?

可能性2

我的第二个想法是将RGB转换为HSV / HSB / HSL(色调,饱和度,值/亮度/亮度),稍微增加亮度,稍微降低饱和度,然后将其转换回RGB。这一般会产生预期的效果吗?

20 个答案:

答案 0 :(得分:53)

作为Wedge said,你想要乘以使事物更亮,但这只有在其中一种颜色变得饱和(即达到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色彩空间。

Lightness gradient with clamping (top) and redistribution (bottom)

为了进行比较,这里是HLS和HSV颜色空间中的相同渐变,由Python的colorsys模块实现。仅修改了L组件,并对生成的RGB值执行了钳制。结果类似,但每个像素都需要色彩空间转换。

Lightness gradient with HLS (top) and HSV (bottom)

答案 1 :(得分:38)

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

答案 2 :(得分:23)

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

我到处都用过这个。

答案 3 :(得分:11)

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

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

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

或者,您可以在Color struct上使用GetHue,GetSaturation,GetBrightness来获取HSB组件。不幸的是,我没有找到逆转换。

答案 4 :(得分: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;
}

答案 5 :(得分: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);

如果您需要更多详情,请read the full story on my blog

答案 6 :(得分:3)

之前曾问过一个非常相似的问题,有用的答案: How do I determine darker or lighter color variant of a given color?

简短回答:如果您只需要“足够好”,则将RGB值乘以常数,如果需要精确度,则转换为HSV。

答案 7 :(得分:3)

我使用安德鲁的回答和马克的答案来this(截至2013年1月,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) }
}

enter image description here

答案 8 :(得分:2)

转换为HS(LVB),增加亮度然后转换回RGB是在不影响色调和饱和度值的情况下可靠地减轻颜色的唯一方法(即仅在不改变任何其他方式的情况下减轻颜色)

答案 9 :(得分:2)

我已经做到了这两种方式 - 你可以用可能性2获得更好的结果。

您为Possibility 1构建的任何简单算法都可能仅适用于有限范围的起始饱和度。

如果(1)您可以限制使用的颜色和亮度,以及(2)您在渲染中执行大量计算,您可能需要查看Poss 1。

生成UI的背景不需要很多阴影计算,所以我建议Poss 2。

-Al。

答案 10 :(得分:1)

派对有点晚,但如果您使用javascriptnodejs,则可以使用tinycolor library,并按照您想要的方式操作颜色:

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

答案 11 :(得分:1)

直到它成为我原来问题的相关问题之前,我才发现这个问题。

然而,使用这些伟大答案的见解。为此我拼凑了一个很好的双线功能:

Programmatically Lighten or Darken a hex color (or rgb, and blend colors)

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

答案 12 :(得分: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)

答案 13 :(得分:1)

方法1:将RGB转换为HSL,调整HSL,转换回RGB。

方法2: Lerp RGB颜色值 - http://en.wikipedia.org/wiki/Lerp_(computing)

有关方法2的C#实现,请参阅my answer to this similar question

答案 14 :(得分:1)

如果你想产生渐变淡出,我建议进行以下优化:不应该为每种颜色做RGB-&gt; HSB-&gt; RGB,而应该只计算目标颜色。一旦知道目标RGB,就可以简单地计算RGB空间中的中间值,而无需来回转换。无论您是否计算使用的线性转换,都取决于您。

答案 15 :(得分:0)

我有一个blog post,它显示了如何在Delphi中执行此操作。它非常简单,因为ColorRGBToHSL和ColorHLSToRGB函数是标准库的一部分。

答案 16 :(得分:0)

您将找到在color-tools ruby library

中的色彩空间之间进行转换的代码

答案 17 :(得分:0)

从技术上讲,我认为两者都不正确,但我相信你想要一个选项#2的变体。问题是采用RGB 990000和“闪电”它真的只会添加到红色通道(值,亮度,亮度),直到你到达FF。之后(稳定的红色),它将取消饱和度一直到纯白色。

转换变得烦人,特别是因为你不能直接进出RGB和Lab,但我认为你真的想要分离色度和亮度值,只需修改亮度就可以真正达到你想要的效果。 / p>

答案 18 :(得分: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))

答案 19 :(得分:0)

我会先尝试#1号,但#2听起来不错。尝试自己动手,看看你对结果是否满意,听起来好像花了10分钟才能完成测试。