web-dev-qa-db-fra.com

Déterminer la couleur de la police en fonction de la couleur de fond

Avec un système (un site Web par exemple) qui permet à l’utilisateur de personnaliser la couleur d’arrière-plan de certaines sections, mais pas la couleur de la police (pour limiter le nombre d’options au minimum), existe-t-il un moyen de déterminer par programme si un "léger" foncé "couleur de police est nécessaire?

Je suis sûr qu'il existe un algorithme, mais je ne connais pas suffisamment les couleurs, la luminosité, etc. pour le comprendre moi-même.

205
Joseph Daigle

J'ai rencontré un problème similaire. Je devais trouver un bon moyen de sélectionner une couleur de police contrastée pour afficher les étiquettes de texte sur les échelles de couleurs/cartes thermiques. Il devait s'agir d'une méthode universelle et la couleur générée devait être "esthétique", ce qui signifie qu'une simple génération de couleurs complémentaires n'était pas une bonne solution - elle générait parfois des couleurs étranges et très intensives difficiles à regarder et à lire.

Après de longues heures de tests et d’essais pour résoudre ce problème, j’ai découvert que la meilleure solution consiste à sélectionner la police blanche pour les couleurs "sombres" et la police noire pour les couleurs "vives". 

Voici un exemple de fonction que j'utilise en C #:

Color ContrastColor(Color color)
{
    int d = 0;

    // Counting the perceptive luminance - human eye favors green color... 
    double luminance = ( 0.299 * color.R + 0.587 * color.G + 0.114 * color.B)/255;

    if (luminance > 0.5)
       d = 0; // bright colors - black font
    else
       d = 255; // dark colors - white font

    return  Color.FromArgb(d, d, d);
}

Ceci a été testé pour de nombreuses échelles de couleurs (arc-en-ciel, niveaux de gris, chaleur, glace, etc.) et constitue la seule méthode "universelle" que j'ai découverte. 

Modifier
Changé la formule de comptage a en "luminance perceptive" - ​​ça a l'air vraiment mieux! Déjà mis en œuvre dans mon logiciel, est superbe.

Edit 2 @ WebSeed a fourni un excellent exemple de travail de cet algorithme: http://codepen.io/WebSeed/full/pvgqEq/

404
Gacek

Merci @Gacek . Voici une version pour Android:

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;

    int d;
    if (a < 0.5) {
        d = 0; // bright colors - black font
    } else {
        d = 255; // dark colors - white font
    }

    return Color.rgb(d, d, d);
}

Et une version améliorée (plus courte):

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
    return a < 0.5 ? Color.BLACK : Color.WHITE;
}
11
Thomas Vos

Juste au cas où quelqu'un voudrait une version plus courte, peut-être plus facile à comprendre, de réponse de GaceK :

public Color ContrastColor(Color iColor)
{
   // Calculate the perceptive luminance (aka luma) - human eye favors green color... 
   double luma = ((0.299 * iColor.R) + (0.587 * iColor.G) + (0.114 * iColor.B)) / 255;

   // Return black for bright colors, white for dark colors
   return luma > 0.5 ? Color.Black : Color.White;
}

Remarque: J'ai supprimé l'inversion de la valeur luma (pour que les couleurs vives aient une valeur supérieure, ce qui me semble plus naturel et constitue également la méthode de calcul «par défaut».

J'ai utilisé les mêmes constantes que GaceK de ici car elles fonctionnaient très bien pour moi.

(Vous pouvez également l'implémenter en tant que Méthode d'extension en utilisant la signature suivante:

public static Color ContrastColor(this Color iColor)

Vous pouvez ensuite l'appeler via foregroundColor = background.ContrastColor().)

9
Marcus Mangelsdorf

Ma mise en œuvre rapide de la réponse de Gacek:

func contrastColor(color: UIColor) -> UIColor {
    var d = CGFloat(0)

    var r = CGFloat(0)
    var g = CGFloat(0)
    var b = CGFloat(0)
    var a = CGFloat(0)

    color.getRed(&r, green: &g, blue: &b, alpha: &a)

    // Counting the perceptive luminance - human eye favors green color...
    let luminance = 1 - ((0.299 * r) + (0.587 * g) + (0.114 * b))

    if luminance < 0.5 {
        d = CGFloat(0) // bright colors - black font
    } else {
        d = CGFloat(1) // dark colors - white font
    }

    return UIColor( red: d, green: d, blue: d, alpha: a)
}
8
vito.royeca

Javascript [ES2015]

const hexToLuma = (colour) => {
    const hex   = colour.replace(/#/, '');
    const r     = parseInt(hex.substr(0, 2), 16);
    const g     = parseInt(hex.substr(2, 2), 16);
    const b     = parseInt(hex.substr(4, 2), 16);

    return [
        0.299 * r,
        0.587 * g,
        0.114 * b
    ].reduce((a, b) => a + b) / 255;
};
6
matfin

C'est une réponse si utile. Merci pour cela!

J'aimerais partager une version SCSS:

@function is-color-light( $color ) {

  // Get the components of the specified color
  $red: red( $color );
  $green: green( $color );
  $blue: blue( $color );

  // Compute the perceptive luminance, keeping
  // in mind that the human eye favors green.
  $l: 1 - ( 0.299 * $red + 0.587 * $green + 0.114 * $blue ) / 255;
  @return ( $l < 0.5 );

}

Déterminez maintenant comment utiliser l'algorithme pour créer automatiquement des couleurs de survol pour les liens de menu. Les en-têtes légers ont un vol stationnaire plus sombre et inversement.

5
ricotheque

Merci pour ce post. 

Pour ceux qui pourraient être intéressés, voici un exemple de cette fonction dans Delphi:

function GetContrastColor(ABGColor: TColor): TColor;
var
  ADouble: Double;
  R, G, B: Byte;
begin
  if ABGColor <= 0 then
  begin
    Result := clWhite;
    Exit; // *** EXIT RIGHT HERE ***
  end;

  if ABGColor = clWhite then
  begin
    Result := clBlack;
    Exit; // *** EXIT RIGHT HERE ***
  end;

  // Get RGB from Color
  R := GetRValue(ABGColor);
  G := GetGValue(ABGColor);
  B := GetBValue(ABGColor);

  // Counting the perceptive luminance - human eye favors green color...
  ADouble := 1 - (0.299 * R + 0.587 * G + 0.114 * B) / 255;

  if (ADouble < 0.5) then
    Result := clBlack;  // bright colors - black font
  else
    Result := clWhite;  // dark colors - white font
end;
5
user3233041

J'ai eu le même problème mais je devais le développer en PHP. J'ai utilisé la solution de @ Garek et j'ai également utilisé cette réponse: Convertir la couleur hexadécimale en valeurs RVB dans PHP pour convertir le code de couleur HEX en RGB.

Donc je le partage.

Je voulais utiliser cette fonction avec une couleur de fond HEX donnée, mais pas toujours à partir de '#'.

//So it can be used like this way:
$color = calculateColor('#804040');
echo $color;

//or even this way:
$color = calculateColor('D79C44');
echo '<br/>'.$color;

function calculateColor($bgColor){
    //ensure that the color code will not have # in the beginning
    $bgColor = str_replace('#','',$bgColor);
    //now just add it
    $hex = '#'.$bgColor;
    list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
    $color = 1 - ( 0.299 * $r + 0.587 * $g + 0.114 * $b)/255;

    if ($color < 0.5)
        $color = '#000000'; // bright colors - black font
    else
        $color = '#ffffff'; // dark colors - white font

    return $color;
}
2
GregV

Ugly Python si vous n'avez pas envie de l'écrire :)

'''
Input a string without hash sign of RGB hex digits to compute
complementary contrasting color such as for fonts
'''
def contrasting_text_color(hex_str):
    (r, g, b) = (hex_str[:2], hex_str[2:4], hex_str[4:])
    return '000' if 1 - (int(r, 16) * 0.299 + int(g, 16) * 0.587 + int(b, 16) * 0.114) / 255 < 0.5 else 'fff'
2
Joseph Coco

Une implémentation pour objectif-c

+ (UIColor*) getContrastColor:(UIColor*) color {
    CGFloat red, green, blue, alpha;
    [color getRed:&red green:&green blue:&blue alpha:&alpha];
    double a = ( 0.299 * red + 0.587 * green + 0.114 * blue);
    return (a > 0.5) ? [[UIColor alloc]initWithRed:0 green:0 blue:0 alpha:1] : [[UIColor alloc]initWithRed:255 green:255 blue:255 alpha:1];
}
1
Andreas Lytter

iOS Swift 3.0 (extension UIColor):

func isLight() -> Bool
{
    if let components = self.cgColor.components, let firstComponentValue = components[0], let secondComponentValue = components[1], let thirdComponentValue = components[2] {
        let firstComponent = (firstComponentValue * 299)
        let secondComponent = (secondComponentValue * 587)
        let thirdComponent = (thirdComponentValue * 114)
        let brightness = (firstComponent + secondComponent + thirdComponent) / 1000

        if brightness < 0.5
        {
            return false
        }else{
            return true
        }
    }  

    print("Unable to grab components and determine brightness")
    return nil
}
1
Josh O'Connor

Mise en œuvre Flutter

Color contrastColor(Color color) {
  if (color == Colors.transparent || color.alpha < 50) {
    return Colors.black;
  }
  double luminance = (0.299 * color.red + 0.587 * color.green + 0.114 * color.blue) / 255;
  return luminance > 0.5 ? Colors.black : Colors.white;
}
0
Mamnarock

Vous pouvez avoir n'importe quel texte de teinte sur n'importe quel fond de teinte et vous assurer qu'il est lisible. Je le fais tout le temps. Il existe une formule pour cela en Javascript sur Texte lisible en couleur - STW * Comme il est dit sur ce lien, la formule est une variante du calcul de l'ajustement gamma inverse, bien qu'un IMHO plus facile à gérer. Les menus situés à droite de ce lien et ses pages associées utilisent des couleurs générées de manière aléatoire pour le texte et l'arrière-plan, toujours lisibles. Alors oui, clairement, cela peut être fait, pas de problème.

0
Dave Collier

Swift 4 Exemple:

extension UIColor {

    var isLight: Bool {
        let components = cgColor.components

        let firstComponent = ((components?[0]) ?? 0) * 299
        let secondComponent = ((components?[1]) ?? 0) * 587
        let thirdComponent = ((components?[2]) ?? 0) * 114
        let brightness = (firstComponent + secondComponent + thirdComponent) / 1000

        return !(brightness < 0.6)
    }

}

UPDATE- Constaté que 0.6 était un meilleur banc d'essai pour la requête 

0
RichAppz

En tant qu'extension Kotlin/Android:

fun Int.getContrastColor(): Int {
    // Counting the perceptive luminance - human eye favors green color...
    val a = 1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255
    return if (a < 0.5) Color.BLACK else Color.WHITE
}
0
Gabriel

Si vous manipulez des espaces colorimétriques pour obtenir un effet visuel, il est généralement plus facile de travailler en HSL (Teinte, Saturation et Luminosité) qu'en RVB. Déplacer des couleurs dans RGB pour obtenir des effets naturellement agréables a tendance à être assez difficile du point de vue conceptuel, alors que convertir en HSL, y manipuler puis reconvertir est plus intuitif dans le concept et donne invariablement de meilleurs résultats. 

Wikipedia a une bonne introduction à HSL et au HSV étroitement lié. Et il existe un code gratuit sur le réseau pour effectuer la conversion (par exemple, voici une implémentation en javascript )

La transformation que vous utilisez est une question de goût, mais personnellement, je pensais qu'inverser les composants Teinte et Légèreté générerait une bonne couleur contrastée en première approximation, mais vous pouvez facilement opter pour des effets plus subtils. 

0
Cruachan

Une variante Android qui capture également l'alpha.

(merci @ thomas-vos)

/**
 * Returns a colour best suited to contrast with the input colour.
 *
 * @param colour
 * @return
 */
@ColorInt
public static int contrastingColour(@ColorInt int colour) {
    // XXX https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color

    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(colour) + 0.587 * Color.green(colour) + 0.114 * Color.blue(colour)) / 255;
    int alpha = Color.alpha(colour);

    int d = 0; // bright colours - black font;
    if (a >= 0.5) {
        d = 255; // dark colours - white font
    }

    return Color.argb(alpha, d, d, d);
}
0
Brill Pappin