简体   繁体   English

屏幕颜色反转如何在OS X中工作?

[英]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: 这就是OS X内置的颜色反转功能可以将您的屏幕转换为:

在此输入图像描述

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? 它是通过OpenGL库完成的吗?

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). 在我的目标应用程序中,我需要定义输出颜色diapasons并应用颜色转换规则(input => output)。

Thanks in advance. 提前致谢。

The way Mac OS does the color inversion is (probably) by using Quartz Display Services to modify the graphics card's gamma table. Mac OS进行颜色反转的方式(可能)是通过使用Quartz Display Services来修改显卡的伽玛表。

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. 其中一个可以被应用程序修改,以改变屏幕显示任何RGB值的方式。

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

To some degree, you can do this with Core Image filters. 在某种程度上,您可以使用Core Image过滤器执行此操作。 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. 但是,这是私有API,因此您需要小心,因为这些内容可能会在未来的OS X版本中发生变化或消失,您显然无法将应用程序提交到App Store。 I don't think something like this is possible with public APIs. 我不认为使用公共API可以做到这一点。

Edit: See Nikolai Ruhe's answer for a better method that uses public APIs. 编辑:请参阅Nikolai Ruhe的答案,了解使用公共API的更好方法。 You can do some things with Core Image filters that you couldn't do with a gamma table (eg applying blur filters and the like), so I'll leave my answer here. 你可以用Core Image过滤器做一些你无法用伽玛表做的事情(例如应用模糊滤镜等),所以我会在这里留下我的答案。

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 ) (改编自Steven Troughton Smith的文章

截图

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). 要影响整个屏幕,您可以创建一个无边框窗口,将ignoresMouseEvents设置为YES (这样您就可以单击它)。

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 这个反向工程标题中有一些关于CGS...函数的信息: http//code.google.com/p/undocumented-goodness/source/browse/trunk/CoreGraphics/CGSPrivate.h

The way OS X itself does it is through a set of undocumented CoreGraphics API calls. OS X本身的方式是通过一组未记录的CoreGraphics API调用。 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: 对其他辅助功能有类似的API调用,例如灰度:

CG_EXTERN bool CGDisplayUsesForceToGray(void);
CG_EXTERN void CGDisplayForceToGray(bool forceToGray);

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,as those same ideas can work in Obj-c. 虽然这在C#中可能是你的基本前提,因为这些相同的想法可以在Obj-c中起作用。

Basically this article says to convert your RGB color in HSV mode then invert the Hue value. 基本上这篇文章说要在HSV模式下转换RGB颜色然后反转Hue值。 To do this you must change Hue by 360-Hue and then convert back to RGB mode. 要执行此操作,您必须通过360-Hue更改Hue,然后转换回RGB模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM