检测显示器是否支持 30 位颜色

Detecting if a display supports 30-bit Color

Apple 最近在 OS X 上启用了 30 位颜色支持。他们发布了一些 sample code 来展示如何启用它。但是,他们似乎没有提供示例来说明如何检测您的应用何时在支持 30 位颜色的显示器上 运行。

我们希望能够检测显示器何时支持 30 位颜色,并且只为支持它的显示器启用 30 位颜色,否则恢复为 24 位颜色。

有人知道怎么做吗?

到目前为止,我已经尝试使用 CGDisplay API(CGDisplayCopyDisplayModeCGDisplayModeCopyPixelEncoding)来查询显示器的像素编码。但是这些似乎总是 return 24 位编码并且 CGDisplayModeCopyPixelEncoding 在 Mac OS X 10.11 中被弃用。我也尝试过使用 NSScreen’s“深度”属性,但这也是 returns 24 位每像素。

内置的系统信息应用程序显然可以获取这些信息,我只是不知道他们是怎么做到的。有什么提示吗?

有各种不好的选择。

首先,如果您记录显示模式(即转换为 id 并传递给 NSLog(@"%@", ...)),您会发现真正的像素编码就在那里。这很有趣,但您真的不想解析该描述。

如果您将 (__bridge CFDictionaryRef)@{ (__bridge NSString*)kCGDisplayShowDuplicateLowResolutionModes: @YES } 作为选项参数传递给 CGDisplayCopyAllDisplayModes(),您会发现您获得了一堆额外的显示模式。 headers 中记录了此密钥,但没有参考文档。对于 Retina 显示屏,一些额外模式是未缩放显示模式的 2 倍缩放对应物。其他是 30-bit-masquerading-as-24 位模式的 24 位对应物。这些在您可以通过 API 查询的所有方面都是相同的,但日志记录显示了不同之处。 (顺便说一下,尝试切换到其中一种模式将会失败。)

我想,但你必须验证,除了 30-bit-color-capable 显示,你没有得到这些 seemingly-identical 模式对。

您或许可以从 IOKit 获取信息。您必须使用已弃用的函数 CGDisplayIOServicePort() 来获取表示 GPU-display 对的 IOFramebuffer object 的服务端口。然后,您可以使用 IORegistryEntrySearchCFProperty() 在服务平面中搜索包含层次结构以找到具有 属性 的 object,例如 "display-bpc" 或 "display-pixel-component-bits" 并获取其价值。至少,在我能够测试的几个系统上有这样的 object 和属性,尽管它们都使用 AMD GPU,并且 属性 在 AMD-specific [=53= 上], 所以不一定靠谱

最后,您可以启动一个子进程到 运行 system_profiler -xml SPDisplaysDataType 并使用 property-list-serialization API 构建一个 属性 列表 object来自结果 XML。然后,您可以在其中找到信息。您可以通过匹配 _spdisplays_display-vendor-idCGDisplayVendorNumber()_spdisplays_display-product-idCGDisplayModelNumber() 以及 _spdisplays_display-serial-numberCGDisplaySerialNumber() 来找到相关显示。然后,深度在键 spdisplays_depth 下,其中值 CGSThirtyBitColor 表示 30 位颜色。

您还应该向 Apple 提交错误报告,请求以合理的方式执行此操作。

从 macOS 10.12 开始,Apple 拥有一些新的 API,可让您检测显示器是否具有宽色域颜色(即深色)。有几种方法可以做到这一点:

  1. 使用 NSScreen 的 - (BOOL)canRepresentDisplayGamut:(NSDisplayGamut)displayGamut

    NSArray<NSScreen *> * screens = [NSScreen screens];
    BOOL hasWideGamutScreen = NO;
    
    for ( NSScreen * screen in screens )
    {
        if ( [screen canRepresentDisplayGamut:NSDisplayGamutP3] )
        {
            hasWideGamutScreen = YES;
            break;
        }
    }
    
  2. 使用CGColorSpaceIsWideGamutRGB(...):

    hasWideGamutScreen = CGColorSpaceIsWideGamutRGB( screen.colorSpace.CGColorSpace );
    
  3. NSWindow 还有 - (BOOL)canRepresentDisplayGamut:(NSDisplayGamut)displayGamut.

当显示器被认为 "wide gamut RGB" 或能够 NSDisplayGamutP3 时,我不知道你是否保证使用支持 30 位的显示器,但这似乎是 Apple 的官方确定显示器是否具有宽色域颜色的方法。

对我有用的是将 NSWindowdepthLimit 属性 传递给 NSBitsPerPixelFromDepth 并检查 return 值是否大于 24。警告:我只有两台 iMac 可以测试,一台是 2012 年的,另一台是 2017 年的,它们都是 运行 High Sierra。