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

后端 未结 4 2075
感动是毒
感动是毒 2021-02-06 13:24

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

\"enter

相关标签:
4条回答
  • 2021-02-06 13:48

    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;
    }
    
    0 讨论(0)
  • 2021-02-06 13:54

    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

    0 讨论(0)
  • 2021-02-06 14:01

    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.

    0 讨论(0)
  • 2021-02-06 14:13

    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);
    
    0 讨论(0)
提交回复
热议问题