Programaticamente clarear uma cor

Motivação

Eu gostaria de encontrar uma maneira de tirar uma cor arbitrária e clarear algumas tonalidades, para que eu possa criar programaticamente um bom gradiente de uma cor para uma versão mais clara. O gradiente será usado como plano de fundo em uma interface do usuário.

Possibilidade 1

Obviamente eu posso simplesmente dividir os valores RGB e aumentá-los individualmente por um certo valor. Isso é realmente o que eu quero?

Possibilidade 2

Meu segundo pensamento foi converter o RGB para HSV / HSB / HSL (matiz, saturação, valor / brilho / luminosidade), aumentar o brilho um pouco, diminuir um pouco a saturação e depois convertê-lo de volta para RGB. Isso terá o efeito desejado em geral?

Eu iria para a segunda opção. De um modo geral, o espaço RGB não é realmente bom para fazer manipulação de colors (criando transição de uma cor para outra, clareando / escurecendo uma cor, etc). Abaixo estão dois sites que eu encontrei com uma rápida pesquisa para converter de / para RGB de / para HSL:

  • dos “Fundamentos da Computação Gráfica”
  • algum código fonte em C # deve ser fácil de se adaptar a outras linguagens de programação.

Como Wedge disse , você quer multiplicar para tornar as coisas mais shinys, mas isso só funciona até que uma das colors fique saturada (ou seja, atinge 255 ou mais). Nesse ponto, você pode simplesmente fixar os valores em 255, mas estará mudando sutilmente o matiz à medida que fica mais claro. Para manter a matiz, você quer manter a relação de (meio-mais baixo) / (mais alto-mais baixo).

Aqui estão duas funções no Python. O primeiro implementa a abordagem ingênua que apenas fixa os valores RGB a 255, se eles passarem. O segundo redistribui os valores em excesso para manter o matiz intacto.

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) 

Eu criei um gradiente começando com o valor RGB (224,128,0) e multiplicando-o por 1,0, 1,1, 1,2, etc. até 2,0. A metade superior é o resultado usando clamp_rgb e a metade inferior é o resultado com redistribute_rgb . Eu acho que é fácil ver que a redistribuição dos overflows dá um resultado muito melhor, sem ter que sair do espaço de colors RGB.

Gradiente de leveza com fixação (superior) e redistribuição (inferior)

Para comparação, aqui está o mesmo gradiente nos espaços de colors HLS e HSV, conforme implementado pelo módulo colorsys do Python. Apenas o componente L foi modificado e a fixação foi realizada nos valores RGB resultantes. Os resultados são semelhantes, mas requerem conversões de espaço de colors para cada pixel.

Gradiente de leveza com HLS (superior) e HSV (inferior)

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

Eu usei isso em todo o lugar.

A class ControlPaint no namespace System.Windows.Forms possui methods estáticos Light e Dark:

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

Esses methods usam a implementação privada do HLSColor. Eu gostaria que esta estrutura fosse pública e em System.Drawing.

Alternativamente, você pode usar GetHue, GetSaturation, GetBrightness em Color struct para obter componentes HSB. Infelizmente, não encontrei a conversão inversa.

Converta-o em RGB e interpole linearmente entre a cor original e a cor alvo (geralmente branca). Então, se você quiser 16 tons entre duas colors, faça:

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

Para obter uma versão mais clara ou mais escura de uma determinada cor, você deve modificar seu brilho. Você pode fazer isso facilmente, mesmo sem converter sua cor para colors HSL ou HSB. Por exemplo, para tornar uma cor mais clara, você pode usar o seguinte código:

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

Se você precisar de mais detalhes, leia a matéria completa no meu blog .

Uma pergunta muito semelhante, com respostas úteis, foi feita anteriormente: Como determinar uma variante de cor mais escura ou mais clara de uma determinada cor?

Resposta curta: multiplique os valores RGB por uma constante, se você só precisa “bom o suficiente”, traduzir para o HSV, se você precisar de precisão.

Eu usei a resposta de Andrew e a resposta de Mark para fazer isso (a partir de 1/2013 sem input de intervalo para 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) } } 

insira a descrição da imagem aqui

A conversão para HS (LVB), aumentando o brilho e convertendo de volta para RGB é a única maneira de aliviar a cor de forma confiável sem afetar os valores de matiz e saturação (ou seja, apenas clarear a cor sem alterá-la de outra maneira).

Eu fiz isso das duas formas – você obtém resultados muito melhores com a Possibilidade 2.

Qualquer algoritmo simples que você construa para a Possibilidade 1 provavelmente funcionará bem apenas para uma faixa limitada de saturações iniciais.

Você gostaria de olhar para Poss 1 se (1) você pode restringir as colors e brilhos usados, e (2) você está realizando o cálculo muito em uma renderização.

Gerar o plano de fundo para uma interface do usuário não precisará de muitos cálculos de sombreamento, então sugiro Poss 2.

-Al.

Se você quiser produzir um fade-out de gradiente, sugiro a seguinte otimização: Em vez de fazer RGB-> HSB-> RGB para cada cor individual, você deve calcular apenas a cor de destino. Uma vez que você conhece o alvo RGB, você pode simplesmente calcular os valores intermediários no espaço RGB sem ter que converter para frente e para trás. Se você calcular uma transição linear de usar algum tipo de curva é com você.

Método 1: Converta RGB para HSL, ajuste HSL, converta de volta para RGB.

Método 2: Lerp os valores de colors RGB – http://en.wikipedia.org/wiki/Lerp_(computing)

Veja minha resposta a esta pergunta semelhante para uma implementação C # do método 2.

Finja que você alfa misturado ao branco:

 oneMinus = 1.0 - amount r = amount + oneMinus * r g = amount + oneMinus * g b = amount + oneMinus * b 

onde amount é de 0 a 1, com 0 retornando a cor original e 1 retornando branco.

Você pode querer misturar-se com qualquer cor do plano de fundo se estiver iluminando para exibir algo desativado:

 oneMinus = 1.0 - amount r = amount * dest_r + oneMinus * r g = amount * dest_g + oneMinus * g b = amount * dest_b + oneMinus * b 

onde (dest_r, dest_g, dest_b) é a cor que está sendo misturada e amount é de 0 a 1, com zero retornando (r, g, b) e 1 retornando (dest.r, dest.g, dest.b)

Eu não encontrei essa pergunta até que se tornou uma questão relacionada à minha pergunta original.

No entanto, usando a visão dessas ótimas respostas. Eu juntei uma function de dois liners para isso:

Programaticamente Clarear ou Escurecer uma cor hexadecimal (ou rgb e misturar colors)

É uma versão do método 1. Mas com mais de saturação levada em conta. Como Keith disse em sua resposta acima; use o Lerp para resolver o mesmo problema que Mark mencionou, mas sem redistribuição. Os resultados de shadeColor2 devem estar muito mais próximos de fazê-lo da maneira correta com o HSL, mas sem a sobrecarga.

Eu teria tentado o número 1 primeiro, mas o # 2 soa muito bem. Tente fazer você mesmo e veja se você está satisfeito com os resultados, parece que você levará cerca de 10 minutos para fazer um teste.

Tecnicamente, acho que não está correto, mas acredito que você queira uma variante da opção 2. O problema é que pegando RGB 990000 e “clareando”, ele realmente adicionaria no canal vermelho (Valor, Brilho, Leveza) até chegar ao FF. Depois disso (vermelho sólido), seria tirando a saturação para ir até o branco sólido.

As conversões se tornam irritantes, especialmente porque você não pode ir direto para RGB e Lab, mas eu acho que você realmente quer separar os valores de crominância e luminância, e apenas modificar a luminosidade para realmente alcançar o que você quer.

Você encontrará o código para converter entre espaços de cor na biblioteca de colors ruby ​​tools

Eu tenho uma postagem no blog que mostra como fazer isso no Delphi. É bem simples porque as funções ColorRGBToHSL e ColorHLSToRGB fazem parte da biblioteca padrão.

Aqui está um exemplo de clareamento de uma cor RGB no Python:

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

Um pouco atrasado para a festa, mas se você usar javascript ou nodejs , você pode usar a biblioteca tinycolor e manipular a cor da maneira que você quiser:

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