47

I know that this won't directly invert a colour, it will just 'oppose' it. I was wondering if anyone knew a simple way (a few lines of code) to invert a colour from any given colour?

At the moment I have this (which isn't exactly the definition of an invert, because if I pass it a grey / gray colour it will return something extremely similar e.g. 127, 127, 127):

const int RGBMAX = 255;

Color InvertMeAColour(Color ColourToInvert)
{
    return Color.FromArgb(RGBMAX - ColourToInvert.R, 
      RGBMAX - ColourToInvert.G, RGBMAX - ColourToInvert.B);
}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Lloyd Powell
  • 18,270
  • 17
  • 87
  • 123

8 Answers8

53

It depends on what do you mean by "inverting" a color

Your code provides a "negative" color.

Are you looking for transform red in cyan, green in purple, blue in yellow (and so on) ? If so, you need to convert your RGB color in HSV mode (you will find here to make the transformation).

Then you just need to invert the Hue value (change Hue by 360-Hue) and convert back to RGB mode.

EDIT: as Alex Semeniuk has mentioned, changing Hue by (Hue + 180) % 360 is a better solution (it does not invert the Hue, but find the opposite color on the color circle)

ThibThib
  • 8,010
  • 3
  • 30
  • 37
  • yup, finally a meaningful answer. +1 – jalf Jul 22 '09 at 13:16
  • 3
    +1 for actually answering my question with the correct solution (rather than providing an alternative to the code I posted) – Lloyd Powell May 18 '11 at 11:09
  • 3
    What happens when Hue=180? The color stays the same? – bfontaine Oct 09 '13 at 12:32
  • 1
    As said by Steve Gilham : "The fixed point theorem implies that any continuous function from (real-valued) Color to Color will leave one value unchanged; if the function is a reasonably smooth one, then a patch around that point won't move much, either." So colors with Hue=180 will not be changed. – ThibThib Oct 18 '13 at 03:32
  • Once again, everything depends on what you need by "inverting" a color, and as Henk Holterman said "check also if the Hue isn't to close to the middle and if so go to either fully bright or fully dark." – ThibThib Oct 18 '13 at 03:41
  • 5
    Incorrect. If you so want to "invert" HSV color by changing `Hue` only, you should use `(Hue + 180) % 360` , not `360 - Hue`. This way you will have no "problems" when your Hue is 180 (not saying that you will receive **correctly** inverted color). – Alex Semeniuk Nov 04 '14 at 06:50
  • I want a solution not debate about what someone meant. – TarmoPikaro Mar 09 '16 at 16:04
  • @AlexSemeniuk is 100% right. I was working with qtcolortriangle trying to get exactly the opposite hue of the current one. int hhh, sss, vvv; hueColor.getHsv(&hhh, &sss, &vvv); //(Hue + 180) % 360 painter.setPen(QPen(curColor.fromHsv((hhh + 180) % 360, sss, vvv), penWidth)); –  Apr 10 '17 at 01:01
  • 1
    Color inversion on a computer screen needs no explanation, it's been a common thing for decades and used with the first WIMPS were invented. I'm not sure why this needs to be explained or why Thib was confused by this. It's quite obvious what colour space and medium the OP is talking about. Your answer is too generic, the OP is looking for a code example.@Remy solution below is much better. – John Stock Sep 24 '20 at 23:27
34

You can use :

MyColor=Color.FromArgb(MyColor.ToArgb()^0xffffff);

It will invert MyColor.

Remy
  • 341
  • 3
  • 2
  • 10
    So, what does that do to grey? 127, 127, 127? – Lloyd Powell Apr 29 '13 at 13:14
  • 6
    It gives a slightly lighter gray: 128, 128, 128 – Jerther Dec 19 '16 at 20:19
  • 2
    I think this is a very good answer for many cases. For example, red will be converted to cyan. This is the vbnet equivalent of Remy's code: `MyColor = Color.FromArgb(MyColor.ToArgb() Xor &Hffffff)` – Alex May 04 '17 at 14:07
11

Try this:

uint InvertColor(uint rgbaColor)
{
    return 0xFFFFFF00u ^ rgbaColor; // Assumes alpha is in the rightmost byte, change as needed
}
Noldorin
  • 144,213
  • 56
  • 264
  • 302
korona
  • 2,308
  • 1
  • 22
  • 37
9

If you want to change every color, try a rotational function (shifting or adding) rather than a flipping function (inverting). In other words, consider the range of 0 to 255 for each single color (red, green, and blue) to be wrapped, connected at the tips like a circle of values. Then shift each color around the cirle by adding some value and doing mod 256. For example, if your starting value for red is 255, and you add 1, you get 0. If you shift all three colors by 128, you get dramatically different values for every original color in the picture, even the grays. Gray 127, 127, 127 becomes white 255, 255, 255. Gray 128, 128, 128 becomes black 0, 0, 0. There's a photographic effect like that called Solarization, discovered by accident by Man Ray in the 1930's.

You can also do rotational operations on each color (red, green, blue) by a different amount to really mess up a picture.

You can also do rotational operations on hue, shifting the hue of every original color by some amount on the hue circle, which alters all the colors without altering the brightness, so the shadows still look like shadows, making people look like Simpsons or Smurphs for example.

The code for a shift by 128 could look like:

public static Color Invert(this Color c) => Color.FromArgb(c.R.Invert(), c.G.Invert(), c.B.Invert());

public static byte Invert(this byte b) {
    unchecked {
        return (byte)(b + 128);
    }
}
Carl Walsh
  • 6,100
  • 2
  • 46
  • 50
Raxin
  • 91
  • 1
  • 1
6

Invert the bits of each component separately:

Color InvertMeAColour(Color ColourToInvert)
{
   return Color.FromArgb((byte)~ColourToInvert.R, (byte)~ColourToInvert.G, (byte)~ColourToInvert.B);
}

EDIT: The ~ operator does not work with bytes automatically, cast is needed.

Kenan E. K.
  • 13,955
  • 3
  • 43
  • 48
  • This throws an exception (ArgumentException "Value of '-129' is not valid for 'red'") – Lloyd Powell Jul 22 '09 at 13:25
  • Now it's been edited that's better, although it still does the same as my function with regards to being a negative, not an invert. thanks for the answer anyway :-) – Lloyd Powell Jul 22 '09 at 13:31
5

What you already have is an RGB-Invert. There are other ways to classify colors and hence other definitions for the Inverse of a Color.

But it sounds like maybe you want a contrasting Color, and there isn't a simple Inversion that is going to work for all colors including RGB(127, 127, 127).

What you need is 1) a conversion to HSV (see ThibThibs answer) and invert the Hue, but also 2) check if the Hue isn't to close to the middle and if so go to either fully bright or fully dark.

H H
  • 263,252
  • 30
  • 330
  • 514
  • 2
    The fixed point theorem implies that any continuous function from (real-valued) Color to Color will leave one value unchanged; if the function is a reasonably smooth one, then a patch around that point won't move much, either. – Steve Gilham Aug 13 '09 at 06:59
1

The most simple and lazy way I made it to work with having not only triple 12x, but mixed values, is this:

Color invertedColor= Color.FromArgb(fromColor.ToArgb() ^ 0xffffff);

if (invertedColor.R > 110 && invertedColor.R < 150 &&
    invertedColor.G > 110 && invertedColor.G < 150 &&
    invertedColor.B > 110 && invertedColor.B < 150)
{
    int avg = (invertedColor.R + invertedColor.G + invertedColor.B) / 3;
    avg = avg > 128 ? 200 : 60;
    invertedColor= Color.FromArgb(avg, avg, avg);
}

Now, invertedColor has a different color that the original, even if we have a 128, 128, 128 or close to it color value.

Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
0

I've created this simple function for VB.NET:

Public Shared Function Invert(ByVal Culoare As Color) As Color
    Return Color.FromArgb(Culoare.ToArgb() Xor &HFFFFFF)
End Function

And this one for C#:

public static Color Invert(Color Culoare)
{
    return Color.FromArgb(Culoare.ToArgb() ^ 0xFFFFFF);
}