9

我正在构建一个基于 CIFilters 和 MetalKit 的实时照片编辑器。但是我遇到了在 MTKView 中显示广色域图像的问题。

标准 sRGB 图像显示得很好,但 Display P3 图像被洗掉了。

我尝试将CIContext.render色彩空间设置为图像色彩空间,但仍然遇到问题。

以下是代码片段:

 guard let inputImage = CIImage(mtlTexture: sourceTexture!) else { return }
                let outputImage = imageEditor.processImage(inputImage)
                print(colorSpace)
                context.render(outputImage,
                               to: currentDrawable.texture,
                               commandBuffer: commandBuffer,
                               bounds: inputImage.extent,
                               colorSpace: colorSpace)
                commandBuffer?.present(currentDrawable)

let pickedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
        print(pickedImage.cgImage?.colorSpace)
        if let cspace = pickedImage.cgImage?.colorSpace {
            colorSpace = cspace
        }

我在 Apple 开发者论坛上发现了类似的问题,但没有任何答案:https ://forums.developer.apple.com/thread/66166

4

1 回答 1

13

为了支持广色域,您需要将 MTKView 设置colorPixelFormatBGRA10_XRbgra10_XR_sRGB。我怀疑colorSpacemacOS MTKViews 的属性在 iOS 上不受支持,因为 iOS 中的颜色管理不是活动的而是有针对性的(请阅读Best practice for color management)。

没有看到您的图像及其实际值,很难诊断,但我会解释我的发现和实验。我建议你像我一样从调试单一颜色开始。

例如,P3 色彩空间中最红的点是什么?可以通过UIColor如下方式定义:

UIColor(displayP3Red: 1, green: 0, blue: 0, alpha: 1)

将 UIButton 添加到您的视图中,并将背景设置为该颜色以进行调试。您可以在代码中获取组件以查看这些值在 sRGB 中的变化,

    var fRed : CGFloat = 0
    var fGreen : CGFloat = 0
    var fBlue : CGFloat = 0
    var fAlpha : CGFloat = 0
    let c = UIColor(displayP3Red: 1, green: 0, blue: 0, alpha: 1)
    c.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha)

或者您可以使用 macOS 颜色同步实用程序中的计算器,

颜色同步实用程序

确保选择Extended Range,否则值将被限制为 0 和 1。

因此,如您所见,您的 P3(1, 0, 0) 对应于扩展 sRGB 中的 (1.0930, -0.2267, -0.1501)。

现在,回到你的MTKView

  • 如果您将 MTKView 的 colorPixelFormat 设置为.BGRA10_XR,那么如果着色器的输出为,您将获得最亮的红色,

    (1.0930, -0.2267, -0.1501)

  • 如果您将colorPixelFormatMTKView 的 设置为.bgra10_XR_sRGB,那么如果您的着色器的输出是,您将获得最亮的红色,

    (1.22486, -0.0420312, -0.0196301)

    因为您必须编写线性 RGB 值,因为这种纹理格式将为您应用伽马校正。应用逆伽玛时要小心,因为有负值。我用这个功能,

    let f = {(c: Float) -> Float in
        if fabs(c) <= 0.04045 {
            return c / 12.92
        }
        return sign(c) * powf((fabs(c) + 0.055) / 1.055, 2.4)
    }
    

最后一个缺失的部分是创建一个广色域UIImage。将颜色空间设置为CGColorSpace.displayP3并复制数据。但是什么数据,对吧?此图像中最亮的红色将是

(1, 0, 0)

或 (65535, 0, 0) 以 16 位整数表示。

我在代码中所做的是使用.rgba16Unorm纹理来处理displayP3色彩空间中的图像,其中 (1, 0, 0) 将是 P3 中最亮的红色。这样,我可以直接将其内容复制到UIImage. 然后,为了显示,我将颜色转换传递给着色器以在显示之前从 P3 转换为扩展的 sRGB(因此,不饱和颜色)。我使用线性颜色,所以我的变换只是一个 3x3 矩阵。我将视图设置为.bgra10_XR_sRGB,因此会自动为我应用伽玛。

那个(列主)矩阵是,

 1.2249  -0.2247  0
-0.0420   1.0419  0
-0.0197  -0.0786  1.0979

你可以在这里阅读我是如何生成它的:Exploring the display-P3 color space

这是我使用 UIButtons 和 MTKView 构建的示例,在 iPhoneX 上截屏,

红色 MTKView

左边的按钮是 sRGB 上最亮的红色,而右边的按钮使用的是 displayP3 颜色。在中心,我放置了一个 MTKView,它输出如上所述的转换后的线性颜色。

同样的绿色实验, 绿色 MTKView

现在,如果您在最近的 iPhone 或 iPad 上看到这一点,您应该会看到中间的正方形和右侧的按钮都具有相同的亮色。如果您在无法显示它们的 Mac 上看到此内容,则左侧按钮将显示相同颜色。如果您在没有适当颜色管理的 Windows 机器或浏览器中看到此问题,左侧按钮也可能显示为不同的颜色,但这只是因为整个图像被解释为 sRGB 并且显然这些像素具有不同的值......但外观不会正确。

如果您需要更多参考,请查看testP3UIColor我在此处添加的单元测试:ColorTests.swift

我初始化的函数UIImageImage.swift

和一个示例应用程序来尝试转换:SampleColorPalette

我没有尝试过CIImages,但我想同样的原则也适用。

我希望这些信息对您有所帮助。我也花了很长时间才弄清楚如何正确显示颜色,因为我在 Metal SDK 文档中找不到对 displayP3 支持的任何明确引用。

于 2018-03-30T17:40:15.300 回答