Question

I'm looking for a way to invert arbitrary NSColor values at runtime, and there doesn't appear to be any built-in method to do so.

I will be using a category to extend NSColor as follows:

NSColor * invertedColor = [someOtherColor inverted];

Here is the category method prototype that I am using:

@implementation NSColor (MyCategories)
- (NSColor *)inverted
{
    NSColor * original = [self colorUsingColorSpaceName:
                      NSCalibratedRGBColorSpace];
    return ...?
}
@end

Can anyone fill in the blanks? This doesn't have to be perfect, but I would like it to make some sense. i.e.:

  1. [[[NSColor someColor] inverted] inverted] would result in a color very close to the original color

  2. [[NSColor whiteColor] inverted] would be very close to [NSColor blackColor]

  3. Inverted colors would be on opposite sides of the color wheel. (red & green, yellow & violet, etc.)

The alpha value should remain the same as the original NSColor. I'm only looking to invert the color, not the transparency.

UPDATE 3: (now in color!)

It turns out that using a complementary color (a color with a hue 180° away from the original hue) is not quite enough, since white and black don't really have a hue value. Starting from the answer provided by phoebus, this is what I came up with:

CGFloat hue = [original hueComponent];
if (hue >= 0.5) { hue -= 0.5; } else { hue += 0.5; }
return [NSColor colorWithCalibratedHue:hue
                            saturation:[original saturationComponent]
                            brightness:(1.0 - [original brightnessComponent])
                                 alpha:[original alphaComponent]];

The hue is still rotated 180°, but I also invert the brightness component so that very dark colors become very bright (and vice versa). This takes care of the black and white cases, but it inverts just about every color to black (which violates my double-inversion rule). Here are the results:

color swatch #1

Now, Peter Hosey's approach is much simpler, and it produces better results:

return [NSColor colorWithCalibratedRed:(1.0 - [original redComponent])
                                 green:(1.0 - [original greenComponent])
                                  blue:(1.0 - [original blueComponent])
                                 alpha:[original alphaComponent]];

color swatch #2

in Swift 4.2:

extension NSColor {

    func inverted() -> NSColor? {
        return NSColor(calibratedRed: 1.0 - redComponent,
                               green: 1.0 - greenComponent,
                               blue: 1.0 - blueComponent,
                               alpha: alphaComponent)
    }
} 
Was it helpful?

Solution

Simple: The components are all 0–1, so subtract each component from 1 to get the complement's value.

Examples:

  • Red = 1, 0, 0; complement = 0, 1, 1 = cyan
  • Yellow = 1, 1, 0; complement = 0, 0, 1 = blue
  • White = 1, 1, 1; complement = 0, 0, 0 = black
  • Deep orange = 1, 0.25, 0; complement = 0, 0.75, 1 = sky blue

As you can guess, this is a symmetric operation. Inverting the inverse will get you exactly the color you started with.

OTHER TIPS

From what you describe, it sounds like what you really want is a function to find a color's complement, not its inverse. Inverting means something different in this context.

Here's a StackOverflow question regarding complements in JavaScript. Perhaps some of the code is adaptable?

Here is some interesting and potentially useful info about the various ways color can be worked with in Cocoa color spaces. From what it looks like here, if you can use the HSV color space, then the complement of a color could be found by simply taking hue > 179 ? hue -= 180 : hue = += 180, since the hues are defined around the color wheel in a full circle.

Here is some oneliner with custom setter (alpha is not reversed!):

-(void)setReverseFontColor:(UIColor *)reverseFontColor {
     _reverseFontColor = [AppSingleStyle reversedColorFromOriginal:reverseFontColor];
}

+(UIColor*)reversedColorFromOriginal:(UIColor*)originalColor {
    return [UIColor colorWithRed:(1-CGColorGetComponents(originalColor.CGColor)[0]) green:(1-CGColorGetComponents(originalColor.CGColor)[1]) blue:(1-CGColorGetComponents(originalColor.CGColor)[2]) alpha:CGColorGetAlpha(originalColor.CGColor)];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow