Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does on-screen color inversion work in OS X?

This is what OS X's built in color inversion feature can turn your screen into:

enter image description here

It can invert all colors, turn them grayscale, adjust contrast. Now I want to build my own implementation and therefore need a professionals' advice.

Inability to capture inverted screen made me thinking that inversion is a sort of an adjustment layer, that resides above all windows and simply is not exposed to the interaction events. Is that so? Is it done via OpenGL libs?

I don't look into actual coding help, but rather a design/approach on solving the problem. In my goal app I will need to define output color diapasons and apply color transformation rules (input => output).

Thanks in advance.

like image 547
Aleksandr Makov Avatar asked Jan 04 '13 19:01

Aleksandr Makov


People also ask

What does invert colors do on Mac?

Inverting the colors on your Macbook's screen is a quick trick that can help prevent eyestrain when reading a stark white page of text for extended periods. It can also help when viewing webpages with text that does not contrast well.

How does color inversion work?

A common means of doing this is via an “invert colours” feature, which flips the bits that make up the pixels on the screen, and you end up with a screen where all the colours are the opposite—white turns to black, light colours turn to dark colours.

How do you invert the colors of a picture on a Mac?

More videos on YouTube Steps: select all, right click, invert color. Preview (Mac): If you're using a Mac, then preview makes inverting colors a breeze. Steps: In the menu, follow tools --> adjust color… . Reverse the dark and light sliders in the adjustment window that opens (as shown in the image above).

What is screen color inversion?

If you find it easier to read light text on a dark background, you can use Android's Colour inversion feature to change the appearance of the screen. This option will invert the colour of the background and any text as well as the colours of images and other interface items.


3 Answers

The way Mac OS does the color inversion is (probably) by using Quartz Display Services to modify the graphics card's gamma table.

Graphics cards have two of these tables to modify color output after composition into the final frame buffer. One of them can be modified by applications to alter the way the screen shows any RGB value.

Here's code to invert the display:

//ApplicationServices includes CoreGraphics
#import <ApplicationServices/ApplicationServices.h>

int main(int argc, const char * argv[])
{
    CGGammaValue table[] = {1, 0};
    CGSetDisplayTransferByTable(CGMainDisplayID(), sizeof(table) / sizeof(table[0]), table, table, table);
    sleep(3);
    return 0;
}
like image 75
Nikolai Ruhe Avatar answered Sep 30 '22 08:09

Nikolai Ruhe


To some degree, you can do this with Core Image filters. However, this is private API, so you need to be careful because these things might change or go away in future OS X releases and you obviously cannot submit your app to the App Store. I don't think something like this is possible with public APIs.

Edit: See Nikolai Ruhe's answer for a better method that uses public APIs. You can do some things with Core Image filters that you couldn't do with a gamma table (e.g. applying blur filters and the like), so I'll leave my answer here.

Here's an example of how to invert what's behind a window:

//Declarations to avoid compiler warnings (because of private APIs):
typedef void * CGSConnection;
typedef void * CGSWindowID;
extern OSStatus CGSNewConnection(const void **attributes, CGSConnection * id);
typedef void *CGSWindowFilterRef;
extern CGError CGSNewCIFilterByName(CGSConnection cid, CFStringRef filterName, CGSWindowFilterRef *outFilter);
extern CGError CGSAddWindowFilter(CGSConnection cid, CGSWindowID wid, CGSWindowFilterRef filter, int flags);
extern CGError CGSSetCIFilterValuesFromDictionary(CGSConnection cid, CGSWindowFilterRef filter, CFDictionaryRef filterValues);

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [self.window setOpaque:NO];
    [self.window setAlphaValue:1.0];
    [self.window setBackgroundColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.1]];
    self.window.level = NSDockWindowLevel;

    CGSConnection thisConnection;
    CGSWindowFilterRef compositingFilter;
    int compositingType = 1; // under the window
    CGSNewConnection(NULL, &thisConnection);
    CGSNewCIFilterByName(thisConnection, CFSTR("CIColorInvert"), &compositingFilter);
    NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:3.0] forKey:@"inputRadius"];
    CGSSetCIFilterValuesFromDictionary(thisConnection, compositingFilter, (CFDictionaryRef)options);
    CGSAddWindowFilter(thisConnection, (CGSWindowID)[self.window windowNumber], compositingFilter, compositingType);    
}

@end

(adapted from Steven Troughton Smith's article here)

screenshot

The effect isn't perfect because for some reason it's necessary that the window has a background color that isn't fully transparent, but it's pretty close.

To affect the whole screen, you could create a borderless window that has ignoresMouseEvents set to YES (so you can click through it).

You can experiment with other filters, but not all of them may work for this. There's some info about the CGS... functions in this reverse-engineered header: http://code.google.com/p/undocumented-goodness/source/browse/trunk/CoreGraphics/CGSPrivate.h

like image 26
omz Avatar answered Sep 30 '22 08:09

omz


The way OS X itself does it is through a set of undocumented CoreGraphics API calls. I don't think they're declared in any official header file, but you can always just declare the prototypes yourself.

// clang -g -O2 -std=c11 -Wall -framework ApplicationServices

#include <stdio.h>
#include <ApplicationServices/ApplicationServices.h>

CG_EXTERN bool CGDisplayUsesInvertedPolarity(void);
CG_EXTERN void CGDisplaySetInvertedPolarity(bool invertedPolarity);

int
main(int argc, char** argv)
{
    bool isInverted = CGDisplayUsesInvertedPolarity();
    printf("isInverted = %d\n", isInverted);

    sleep(2);
    CGDisplaySetInvertedPolarity(!isInverted);
    printf("Polarity is now: %d\n", CGDisplayUsesInvertedPolarity());

    sleep(2);
    CGDisplaySetInvertedPolarity(isInverted);
    printf("Polarity is now: %d\n", CGDisplayUsesInvertedPolarity());

    return 0;
}

There are similar API calls for other accessibility features, such as grayscale:

CG_EXTERN bool CGDisplayUsesForceToGray(void);
CG_EXTERN void CGDisplayForceToGray(bool forceToGray);
like image 25
Ryan Schmitt Avatar answered Sep 30 '22 09:09

Ryan Schmitt