MTKView 显示广色域 P3 色彩空间
Posted
技术标签:
【中文标题】MTKView 显示广色域 P3 色彩空间【英文标题】:MTKView Displaying Wide Gamut P3 Colorspace 【发布时间】:2017-12-30 22:20:22 【问题描述】:我正在构建一个基于 CIFilters 和 MetalKit 的实时照片编辑器。但我遇到了在 MTKView 中显示广色域图像的问题。
标准 sRGB 图像显示正常,但 Display P3 图像褪色。
我尝试将 CIContext.render
颜色空间设置为图像颜色空间,但仍然遇到问题。
这里是sn-ps的代码:
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
【问题讨论】:
我不确定它在 ios 中是否可行...在 macOS 中,MTKView 有一个您可以设置的 colorSpace 属性,但显然它在 iOS 中不受支持:(developer.apple.com/documentation/metalkit/mtkview/… 【参考方案1】:为了支持广色域,您需要将 MTKView 的colorPixelFormat
设置为BGRA10_XR 或bgra10_XR_sRGB。我怀疑 macOS MTKViews 的 colorSpace
属性在 iOS 上不受支持,因为 iOS 中的颜色管理不是活动而是有针对性的(阅读Best practices 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 颜色同步实用程序中的计算器,
确保选择扩展范围,否则值将被限制为 0 和 1。
因此,如您所见,您的 P3(1, 0, 0) 对应于扩展 sRGB 中的 (1.0930, -0.2267, -0.1501)。
现在,回到你的MTKView
,
如果您将 MTKView 的 colorPixelFormat 设置为 .BGRA10_XR
,那么如果您的着色器的输出是,您将获得最亮的红色,
(1.0930, -0.2267, -0.1501)
如果您将 MTKView 的 colorPixelFormat
设置为 .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 上截屏,
左边的按钮是 sRGB 上最亮的红色,而右边的按钮使用的是 displayP3 颜色。在中心,我放置了一个 MTKView,它输出如上所述的转换后的线性颜色。
同样的绿色实验,
现在,如果您在最近的 iPhone 或 iPad 上看到此内容,您应该会看到中间的正方形和右侧的按钮都具有相同的亮色。如果您在无法显示它们的 Mac 上看到此内容,则左侧按钮将显示相同颜色。如果您在没有适当颜色管理的 Windows 机器或浏览器中看到这一点,左侧按钮也可能看起来具有不同的颜色,但这只是因为整个图像被解释为 sRGB 并且显然这些像素具有不同的值......但外观不会正确。
如果您需要更多参考,请查看我在此处添加的testP3UIColor
单元测试:ColorTests.swift,
我的函数来初始化UIImage
:Image.swift,
以及一个用于尝试转换的示例应用:SampleColorPalette
我还没有尝试过CIImages
,但我想同样的原则也适用。
我希望这些信息对您有所帮助。我还花了很长时间才弄清楚如何正确显示颜色,因为我在 Metal SDK 文档中找不到对 displayP3 支持的任何明确引用。
【讨论】:
其实1.22486, -0.0420312, -0.0196301
不是bgra10_xr_srgb
像素格式最亮的红色,1.358, -0.074, -0.012
才是。 Justin Stoyles 说,在Working with Wide Color 会话中,您可以通过在MTKView
s 之间放置UIView
和1.0, 0.0, 0.0, 1.0
Display P3 背景颜色来查看1.22486, -0.0420312, -0.0196301
和1.358, -0.074, -0.012
颜色。
看来bgra10_xr_srgb
像素格式使用的颜色空间与CGColorSpace.extendedSRGB
或CGColorSpace.extendedLinearSRGB
不同。对于将转换矩阵从 Display P3 获取到该色彩空间的任何帮助,我将不胜感激。
感谢您的链接。不确定1.358
来自哪里。如果创建 displayP3 UIColor,let c = UIColor(displayP3Red: 1, green: 0, blue: 0, alpha: 1); c.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha)
,红色值为1.0930339097976685
。要让它在转换为线性空间时变为1.358
,你需要一个gamma=log(1.358)/log(1.093)=3.44
的伽玛,它大得离谱。我敢打赌他们正在使用这种常见的伽马计算:linear=((c+0.055)/1.055)^2.4
,他们忘记了除以 1.055 或其他东西。胡思乱想……以上是关于MTKView 显示广色域 P3 色彩空间的主要内容,如果未能解决你的问题,请参考以下文章
AVCaptureVideoDataOutput P3 色彩空间
ERROR ITMS-90682: Invalid Bundle - The asset catalog at 'Payload/XXXXX/Assets.car' can't