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

若如初见. 提交于 2020-01-01 01:13:10

问题


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

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.


回答1:


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;
}



回答2:


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)

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




回答3:


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);



回答4:


This article explains how another user inverted his colors, however that wouldn't be an overlay, it would still accomplish your goal. Although this is in C# that would be your basic premise perhaps,a s those same ideas can work in Obj-c.

Basically this article says to convert your RGB color in HSV mode then invert the Hue value. To do this you must change Hue by 360-Hue and then convert back to RGB mode.



来源:https://stackoverflow.com/questions/14163788/how-does-on-screen-color-inversion-work-in-os-x

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!