Motivation
Je voudrais trouver un moyen de prendre une couleur arbitraire et de l'éclaircir de quelques nuances, afin que je puisse créer par programme un joli dégradé d'une couleur à une version plus claire. Le dégradé sera utilisé comme arrière-plan dans une interface utilisateur.
possibilité 1
Évidemment, je peux simplement diviser les valeurs RVB et les augmenter individuellement d'une certaine quantité. Est-ce vraiment ce que je veux?
possibilité 2
Ma deuxième pensée a été de convertir le RVB en HSV/HSB/HSL (teinte, saturation, valeur/luminosité/luminosité), d'augmenter un peu la luminosité, de diminuer un peu la saturation, puis de la reconvertir en RVB. Cela aura-t-il l'effet souhaité en général?
J'irais pour la deuxième option. De manière générale, l'espace RVB n'est pas vraiment bon pour faire de la manipulation des couleurs (créer une transition d'une couleur à une autre, éclaircir/assombrir une couleur, etc.). Voici deux sites que j'ai trouvés avec une recherche rapide pour convertir de/vers RVB vers/depuis HSL:
Comme Wedge a dit , vous voulez multiplier pour rendre les choses plus lumineuses, mais cela ne fonctionne que jusqu'à ce qu'une des couleurs devienne saturée (c'est-à-dire frappe 255 ou plus). À ce stade, vous pouvez simplement fixer les valeurs à 255, mais vous changerez subtilement la teinte à mesure que vous deviendrez plus clair. Pour conserver la teinte, vous souhaitez conserver le rapport (moyen-bas)/(plus haut-bas).
Voici deux fonctions en Python. Le premier implémente l'approche naïve qui fixe simplement les valeurs RVB à 255 si elles dépassent. Le second redistribue les valeurs excédentaires pour garder la teinte intacte.
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)
J'ai créé un dégradé en commençant par la valeur RVB (224,128,0) et en le multipliant par 1,0, 1,1, 1,2, etc. jusqu'à 2,0. La moitié supérieure est le résultat en utilisant clamp_rgb
et la moitié inférieure est le résultat avec redistribute_rgb
. Je pense qu'il est facile de voir que la redistribution des débordements donne un bien meilleur résultat, sans avoir à quitter l'espace colorimétrique RVB.
À titre de comparaison, voici le même dégradé dans les espaces colorimétriques HLS et HSV, tel qu'implémenté par le module colorsys
de Python. Seul le composant L
a été modifié et le serrage a été effectué sur les valeurs RVB résultantes. Les résultats sont similaires, mais nécessitent des conversions d'espace colorimétrique pour chaque pixel.
En 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) );
}
Je l'ai utilisé partout.
La classe ControlPaint dans l'espace de noms System.Windows.Forms a des méthodes statiques Light et Dark:
public static Color Dark(Color baseColor, float percOfDarkDark);
Ces méthodes utilisent une implémentation privée de HLSColor. Je souhaite que cette structure soit publique et dans System.Drawing.
Alternativement, vous pouvez utiliser GetHue, GetSaturation, GetBrightness on Color struct pour obtenir les composants HSB. Malheureusement, je n'ai pas trouvé la conversion inverse.
Convertissez-le en RVB et interpolez linéairement entre la couleur d'origine et la couleur cible (souvent le blanc). Donc, si vous voulez 16 teintes entre deux couleurs, vous faites:
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;
}
Afin d'obtenir une version plus claire ou plus foncée d'une couleur donnée, vous devez modifier sa luminosité. Vous pouvez le faire facilement même sans convertir votre couleur en couleur HSL ou HSB. Par exemple, pour éclaircir une couleur, vous pouvez utiliser le code suivant:
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);
Si vous avez besoin de plus de détails, lire l'histoire complète sur mon blog .
Une question très similaire, avec des réponses utiles, a été posée précédemment: Comment puis-je déterminer la variante de couleur plus foncée ou plus claire d'une couleur donnée?
Réponse courte: multipliez les valeurs RVB par une constante si vous avez juste besoin de "assez bien", traduisez en HSV si vous avez besoin de précision.
J'ai utilisé la réponse d'Andrew et la réponse de Mark pour faire this (à partir de 1/2013 aucune entrée de plage pour 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) }
}
La conversion en HS (LVB), l'augmentation de la luminosité, puis la reconversion en RVB est le seul moyen d'éclaircir de manière fiable la couleur sans affecter les valeurs de teinte et de saturation (c'est-à-dire d'éclaircir uniquement la couleur sans la changer de toute autre manière).
J'ai fait cela dans les deux sens - vous obtenez de bien meilleurs résultats avec la possibilité 2.
Tout algorithme simple que vous construisez pour la possibilité 1 ne fonctionnera probablement bien que pour une plage limitée de saturations de démarrage.
Vous voudriez examiner Poss 1 si (1) vous pouvez restreindre les couleurs et les luminosités utilisées, et (2) vous effectuez beaucoup le calcul dans un rendu.
Générer l'arrière-plan d'une interface utilisateur ne nécessitera pas beaucoup de calculs d'ombrage, donc je suggère Poss 2.
-Al.
Un peu tard pour la fête, mais si vous utilisez javascript
ou nodejs
, vous pouvez utiliser bibliothèque tinycolor , et manipuler la couleur comme vous le souhaitez:
tinycolor("red").lighten().desaturate().toHexString() // "#f53d3d"
Je n'ai trouvé cette question qu'après qu'elle soit devenue une question liée à ma question d'origine.
Cependant, en utilisant la perspicacité de ces grandes réponses. J'ai reconstitué une fonction Nice à deux lignes pour cela:
Éclaircir ou assombrir par programme une couleur hexadécimale (ou RVB et mélanger les couleurs)
C'est une version de la méthode 1. Mais avec une sursaturation prise en compte. Comme l'a dit Keith dans sa réponse ci-dessus; utiliser Lerp pour résoudre apparemment le même problème que Mark mentionné, mais sans redistribution. Les résultats de shadeColor2
devrait être beaucoup plus proche de le faire correctement avec HSL, mais sans la surcharge.
Méthode 1: convertir RVB en HSL, ajuster HSL, reconvertir en RVB.
Méthode 2: Lerp les valeurs de couleur RVB - http://en.wikipedia.org/wiki/Lerp_ (informatique)
Voir ma réponse à cette question similaire pour une implémentation C # de la méthode 2.
Imaginez que vous alpha mélangé au blanc:
oneMinus = 1.0 - amount
r = amount + oneMinus * r
g = amount + oneMinus * g
b = amount + oneMinus * b
où amount
est compris entre 0 et 1, 0 renvoyant la couleur d'origine et 1 renvoyant le blanc.
Vous voudrez peut-être vous fondre avec la couleur d'arrière-plan si vous éclaircissez pour afficher quelque chose de désactivé:
oneMinus = 1.0 - amount
r = amount * dest_r + oneMinus * r
g = amount * dest_g + oneMinus * g
b = amount * dest_b + oneMinus * b
où (dest_r, dest_g, dest_b)
est la couleur à laquelle se fondre et amount
va de 0 à 1, zéro renvoyant (r, g, b)
et 1 renvoyant (dest.r, dest.g, dest.b)
SI vous voulez produire un dégradé en fondu, je suggère l'optimisation suivante: Plutôt que de faire RVB-> HSB-> RVB pour chaque couleur individuelle, vous ne devez calculer que la couleur cible. Une fois que vous connaissez le RVB cible, vous pouvez simplement calculer les valeurs intermédiaires dans l'espace RVB sans avoir à effectuer de conversion dans les deux sens. Que vous calculiez une transition linéaire d'utilisation, une sorte de courbe dépend de vous.
J'aurais essayé le numéro 1 en premier, mais le numéro 2 sonne plutôt bien. Essayez de le faire vous-même et voyez si vous êtes satisfait des résultats, il semble que cela vous prendra peut-être 10 minutes pour concocter un test.
Techniquement, je ne pense pas que ce soit correct, mais je pense que vous voulez une variante de l'option # 2. Le problème étant que, prenant RGB 990000 et "éclaircissant", cela ajouterait vraiment juste sur le canal rouge (valeur, luminosité, luminosité) jusqu'à ce que vous arriviez à FF. Après cela (rouge uni), cela réduirait la saturation pour aller jusqu'au blanc uni.
Les conversions deviennent ennuyeuses, d'autant plus que vous ne pouvez pas aller directement de et vers RGB et Lab, mais je pense que vous voulez vraiment séparer les valeurs de chrominance et de luminance, et simplement modifier la luminence pour vraiment atteindre ce que vous voulez.
Voici un exemple d'éclaircissement d'une couleur RVB en Python:
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))
J'ai un article de blog qui montre comment faire cela dans Delphi. C'est assez simple car les fonctions ColorRGBToHSL et ColorHLSToRGB font partie de la bibliothèque standard.
Vous trouverez du code pour convertir entre les espaces colorimétriques dans la color-tools Ruby library