Reverse opposing colors

37,017

Solution 1

I found that the best solution for me is to convert the RGB values into YIQ values. As we are only interested in the brightness value (represented by Y), there is one single calculation to be done: Y = (299*R + 587*G + 114*B)/1000. The Java code for that would look like this:

public static Color getContrastColor(Color color) {
  double y = (299 * color.getRed() + 587 * color.getGreen() + 114 * color.getBlue()) / 1000;
  return y >= 128 ? Color.black : Color.white;
}

You can see that it simply decides to use black or white, based upon the brightness of the original color. And the result works very nice in my opinion. The weights (299, 587, 114) are proportional to the sensitivity of the eyes (or rather the sensitivity of the retina) to the corresponding color.

Solution 2

Use complementary color:

Algo is simple, substract each color component from 255 to get new color components

Color textColor = Color.rgb(255-Color.red(bgColor),
                         255-Color.green(bgColor),
                         255-Color.blue(bgColor));

----- EDIT (As RGB based complement may not work always --------

These two links are very much helpful and on topic:

http://www.splitbrain.org/blog/2008-09/18-calculating_color_contrast_with_php

http://24ways.org/2010/calculating-color-contrast

Solution 3

Based on Marks solution I would suggest:

public static int getComplementaryColor(int colorToInvert) {
    float[] hsv = new float[3];
    Color.RGBToHSV(Color.red(colorToInvert), Color.green(colorToInvert),
            Color.blue(colorToInvert), hsv);
    hsv[0] = (hsv[0] + 180) % 360;
    return Color.HSVToColor(hsv);
}

And additionally I now created a similar method, for calculating a default background for a given color:

public static int getContrastVersionForColor(int color) {
    float[] hsv = new float[3];
    Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color),
            hsv);
    if (hsv[2] < 0.5) {
        hsv[2] = 0.7f;
    } else {
        hsv[2] = 0.3f;
    }
    hsv[1] = hsv[1] * 0.2f;
    return Color.HSVToColor(hsv);
}

Solution 4

As Sarwar Erfan pointed out, use complementary colors. For that, you can use an integer mask (which will be faster, than inverting R, G, B color components separately).

int textColor = bgColor ^ 0x00ffffff;

Solution 5

integer solution:

public static int getContrastColor(int color) {
        double y = (299 * Color.red(color) + 587 * Color.green(color) + 114 * Color.blue(color)) / 1000;
        return y >= 128 ? Color.BLACK : Color.WHITE;
    }
Share:
37,017

Related videos on Youtube

Mark Worsnop
Author by

Mark Worsnop

Updated on July 09, 2022

Comments

  • Mark Worsnop
    Mark Worsnop almost 2 years

    I have a user setup where they can choose the colors of the alerts. the Alert is the background color on a text or button. But the problem comes in that if they select a dark blue and we have black letters the contrast isnt enough and you cannot read it.

    I have tried to make a function to get the reverse opposing color but havent got too far.

    Is there such a function?

    • Nemi
      Nemi over 13 years
      There are at least two questions on here for this. One is stackoverflow.com/questions/596216/…. I know because I implemented an algorithm from one of them and it works well.
  • Anthony
    Anthony over 13 years
    Doesn't this give a big issue when the user selects the color 128/128/128 (or something really near).
  • Sarwar Erfan
    Sarwar Erfan over 13 years
    I have included a link from Wikipedia in the answer. RGB based complement is easiest to understand. HSV based complement will work in this case. @Shynhriir: Yes.. RGB based complement will not work in this range, you are absolutely right
  • Mark Worsnop
    Mark Worsnop over 13 years
    How would you code into Android the complementary color from the Wiki link?
  • Sarwar Erfan
    Sarwar Erfan over 13 years
    @Mark: It is not very hard. I have previously wrote my own 3d perspective transformation code just reading the corresponding theories.
  • Mark Worsnop
    Mark Worsnop over 13 years
    If you are new to Java and Android it is :) I got it though... see above
  • Sarwar Erfan
    Sarwar Erfan over 13 years
    @Mark: You have future I see.
  • Hoang Tran
    Hoang Tran almost 11 years
    Thanks. This may not cover all cases, but is pefect for my usecase (make text readable on colored background)
  • brimborium
    brimborium almost 11 years
    You always can read the text pretty good. The disadvantage is, that the text will be black or white, color information of the background is "lost".
  • Simon
    Simon over 9 years
    why that "RGBMAX - "? and the hue is a value between 0 and 360 and not 0 and 1 I think, i postet an updated version below
  • Shajeel Afzal
    Shajeel Afzal over 8 years
    You getComplementaryColor is working perfectly but not getContrastVersionForColor can you please fix?
  • Simon
    Simon over 8 years
    What do you mean it does not work? You don't like the result? Can you provide a color int value for which it does not look right? I use it quite often and never had problems
  • Phani
    Phani over 8 years
    Can you please explain on how it would solve the problem ?
  • Eboo
    Eboo over 8 years
    "I have tried to make a function to get the reverse opposing color but havent got too far. " So here is a function ^^ it return black or white color.
  • Phani
    Phani over 8 years
    Going back to first question..if it doesn't resolve the issue..then delete it.. or else update the question with latest findings.
  • Emmanuel Istace
    Emmanuel Istace almost 6 years
    OMFG, I have been messing with RGB/HSV conversion and hue shifting for hours without "nice" sucess and this is by far the best and so easy solution I've found for my need. Thanks.
  • brimborium
    brimborium over 5 years
    I like this solution a lot. A perfect alternative if you don't like the black/white YIQ approach.
  • Frontear
    Frontear almost 5 years
    @brimborium has already posted an extremely similar response 3 years before yours.
  • Mustafa
    Mustafa almost 4 years
    In JavaFx the value y will be between 0 and 1. So the last line of this method should look like this return y >= 0.5 ? Color.valueOf("#000000") : Color.valueOf("#ffffff");
  • ZaFaR97
    ZaFaR97 about 2 years
    To make it works on Android apps, I edit it: ``` private int getContrastColor(int color) { double y = (299 * Color.red(color) + 587 * Color.green(color) + 114 * Color.blue(color)) / 1000; return y >= 128 ? Color.BLACK : Color.WHITE; }```