Is there a conventional method for inverting NSColor values?
Asked Answered
N

3

19

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)
    }
} 
Nucleoside answered 27/1, 2010 at 6:27 Comment(0)
O
22

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.

Octahedral answered 27/1, 2010 at 6:59 Comment(1)
Note: For grey = 0.5, 0.5, 0.5; complement is itself.Porphyroid
E
3

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.

Euphroe answered 27/1, 2010 at 6:37 Comment(1)
And thank you for the point about complementary colors vs inverted colors. I'll add that to the question.Nucleoside
M
1

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)];
}
Masthead answered 7/7, 2013 at 13:10 Comment(1)
you might want to consider using const CGFloat *cc = CGColorGetComponents(originalColor.CGColor); return [UIColor colorWithRed:(1-cc[0]) green:(1-cc[1]) blue:(1-cc[2]) alpha:CGColorGetAlpha(originalColor.CGColor)]; (why call a function 3 times to get the same result?)Penland

© 2022 - 2024 — McMap. All rights reserved.