18

Is there a way to find the complement of a color given its RGB values? Or can it only be found for certain colors? How would someone go about doing this in Java?

Mohit Deshpande
  • 53,877
  • 76
  • 193
  • 251
  • Define complement / supplement colours. If the complement and supplement are always relative to the input colour (depending on your definition), it should be easy. I've seen it done on the web with Javascript, I just can't remember the name or URL. – FrustratedWithFormsDesigner Jun 16 '10 at 15:47
  • Update: I found one! http://colorschemedesigner.com/ It doesn't do supplement colours, but has a whole bunch of other choices. – FrustratedWithFormsDesigner Jun 16 '10 at 15:48
  • Complementary is easy using the [HSL Color](http://tips4java.wordpress.com/2009/07/05/hsl-color/) class. – camickr Jun 16 '10 at 16:22

7 Answers7

10

This is what I come up: (Conjecture)

Let r, g, and b be RGB components of the original color.

Let r', g', and b' be RGB components of the complementary color.

Then:

r' = max(r,b,g) + min(r,b,g) - r   
b' = max(r,b,g) + min(r,b,g) - b
g' = max(r,b,g) + min(r,b,g) - g

I see that this gives the same answer to what the websites (i.e. www.color-hex.com) give, but I will still prove it. :)

EDIT (23/07/2023): Yeah this old "immature" answer of mine needs long-due explanation:

  • This formula is correct only in the RGB model, which means that yes, Red pairs with Cyan. If you want to pair Red with Green instead, you need to look at RYB model instead.
  • This formula is equivalent to applying HSV/HSL to input RGB, then rotate 180 degrees, then convert back to RGB. Of course, this conversion process is what you should do if you need to compute more than just the complementary color.
  • The following is still not a proof, but more of an intuition of why this works: imagine this picture and each hue is a vertical slice, and thus with corresponding level of red, blue, green in [0,1]. Finding complementary color is essentially "setting what is high, to low" and vice versa. If the red in input is the highest, then red in the complementary should be the lowest. More generally, if the blue in input is x above the lowest, then the blue in complementary should be x below the highest. From all of this, the formula can be derived.
Poypoyan
  • 446
  • 6
  • 15
4

A more general and simple solution for finding the opposite color is:

private int getComplementaryColor( int color) {
    int R = color & 255;
    int G = (color >> 8) & 255;
    int B = (color >> 16) & 255;
    int A = (color >> 24) & 255;
    R = 255 - R;
    G = 255 - G;
    B = 255 - B;
    return R + (G << 8) + ( B << 16) + ( A << 24);
}
tm1701
  • 7,307
  • 17
  • 79
  • 168
2

For a rough approximation, you can do this by converting RGB to HSL (Hue, Saturation, Lightness).

With the HSL value, shift the hue 180 degrees to get a color on the opposite of the color wheel from the original value.

Finally, convert back to RGB.

I've written a JavaScript implementation using a hex value here - https://stackoverflow.com/a/37657940/4939630

For RGB, simply remove the hex to RGB and RGB to hex conversions.

Queeg
  • 7,748
  • 1
  • 16
  • 42
Edward
  • 5,148
  • 2
  • 29
  • 42
  • although it sounds tempting to find something on the opposite side of the color wheel, colors that are too close to the center do not come up with good contrast colors. – Queeg Jul 22 '23 at 17:28
0

None of the answers above really give a way to find the complimentary color, so here's my version written in Processing:

import java.awt.Color;

color initial = color(100, 0, 100);

void setup() {
  size(400, 400);  

  fill(initial);
  rect(0, 0, width/2, height);

  color opposite = getOppositeColor(initial);
  fill(opposite);
  rect(width/2, 0, width/2, height);
}

color getOppositeColor(color c) {
  float[] hsv = new float[3];
  Color.RGBtoHSB(c>>16&0xFF, c>>8&0xFF, c&0xFF, hsv);
  hsv[0] = (hsv[0] + 0.5) % 1.0;

  // black or white? return opposite
  if (hsv[2] == 0) return color(255);
  else if (hsv[2] == 1.0) return color(0);

  // low value? otherwise, adjust that too
  if (hsv[2] < 0.5) {
    hsv[2] = (hsv[2] + 0.5) % 1.0;
  }

  return Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]);
}
JeffThompson
  • 1,538
  • 3
  • 24
  • 47
0

Just focusing on the 3D RGB cube. Here's a function that does a binary search in that cube to avoid having to figure out how to cast vectors in a direction and find the closest point on the cube boundary that intersects it.

def farthestColorInRGBcubeFrom(color):
  def pathsAt(r, g, b):
    paths = [(r, g, b)]
    if r < 255:
        paths.append((int((r + 255)/2), g, b))
    if r > 0:
        paths.append((int(r/2), g, b))
    if g < 255:
        paths.append((r, int((g + 255)/2), b))
    if g > 0:
        paths.append((r, int(g/2), b))
    if b < 255:
        paths.append((r, g, int((b + 255)/2)))
    if b > 0:
        paths.append((r, g, int(b/2)))
    return paths                         
r = color.red();  g = color.green();  b = color.blue();
#naive guess:
r0 = 255 - r;  g0 = 255 - g;  b0 = 255 - b;
paths = pathsAt(r0, g0, b0)
maxPath = None
while paths != []:
    for p in paths:
        d = (r - p[0])**2 + (g - p[1])**2 + (b - p[0])**2
        if maxPath != None:
            if d > maxPath[0]:
                maxPath = (d, p)
        else:
            maxPath = (d, p)
    p = maxPath[1]
    paths = pathsAt(p[0], p[1], p[2])
c = maxPath[1]
return QColor(c[0], c[1], c[2], color.alpha())
-1

You might find more information at: Programmatically choose high-contrast colors

public static int complimentaryColour(int colour) {
    return ~colour;
}
Brill Pappin
  • 4,692
  • 1
  • 36
  • 36
-3

it should just be math

the total of the r values of the 2 colors should be 255

the total of the g values of the 2 colors should be 255

the total of the b values of the 2 colors should be 255

Randy
  • 16,480
  • 1
  • 37
  • 55