Windows 的色彩管理一直是一个非常令人诟病的问题,甚至已经把人逼到去 hook dwm。在当前版本的 Windows 10/11 中,微软静悄悄地加入了两个色彩管理有关的功能:
硬件实现的色彩空间转换其实已经不是新鲜事,当前的 GPU 硬件基本都支持在 RGB 通道间进行线性变换,更有甚者提供了 3DLUT ,驱动也提供了对应的用户态 API ,或进一步包装成色彩空间转换 API 。( API 参考:NVIDIA、AMD、Intel)
而在 Linux drm 框架下,硬件矩阵变换功能对应统一的 drm_color_ctm API ,可以从用户态直接访问,或通过 XRandR CTM API 访问。
后来 WDDM 规范中加入了矩阵变换作为可选功能,但是并没有说明如何从用户态调用这个功能。
在 Windows 11 SDK 中,微软提供了一个没有文档、甚至没有对应导出函数的 API:
// C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um\Icm.h:1481
#if NTDDI_VERSION >= NTDDI_WIN10_CO
struct WCS_DEVICE_VCGT_CAPABILITIES
{
UINT Size; // Size of structure in bytes
BOOL SupportsVcgt; // Indicates if display supports VCGT
};
struct WCS_DEVICE_MHC2_CAPABILITIES
{
UINT Size; // Size of structure in bytes
BOOL SupportsMhc2; // Indicates if display supports MHC2
UINT RegammaLutEntryCount; // Max number of entries in the regamma lut
// Color space transform (CSC) matrix (row-major)
UINT CscXyzMatrixRows; // Number of rows in the color transform matrix
UINT CscXyzMatrixColumns; // Number of columns in the color transform matrix
};
typedef enum
{
VideoCardGammaTable = 1,
MicrosoftHardwareColorV2 = 2,
} WCS_DEVICE_CAPABILITIES_TYPE;
/*
* Gets the selected display color correction capabilities
*
* Parameters:
* scope specifies the association as system-wide or user-specific
* targetAdapterID the target adapter of the display
* sourceID the source identifier of the display
* capsType the type of capabilities to retrieve (VCGT, MHC2)
* outputCapabilities the color correction capabilities to return
*/
HRESULT WINAPI ColorProfileGetDeviceCapabilities(
_In_ WCS_PROFILE_MANAGEMENT_SCOPE scope,
_In_ LUID targetAdapterID,
_In_ UINT32 sourceID,
_In_ WCS_DEVICE_CAPABILITIES_TYPE capsType,
_Out_ PVOID outputCapabilities
);
#endif //NTDDI_VERSION >= NTDDI_WIN10_CO
与此同时,新上市的广色域(指超出 sRGB )笔记本 /显示器附带的 ICC 文件中还包含了一个叫做 MHC2
的神秘 tag ,合理怀疑是与上面的 MicrosoftHardwareColorV2
有关。通过横向对比多个 ICC 文件中的 MHC2
tag 以及一些简单的逆向分析,再结合 SDK 中提供的信息,得出这个 tag 中存放的是屏幕的最高 /最低亮度,以及用于色彩空间转换的矩阵( 4x3 ,猜测是多了一列 offset )和 regamma LUT 。
鉴于 Windows 应用默认 sRGB 色彩空间,我们可以求得一个矩阵将 sRGB 的三通道亮度转换到设备色彩空间的三通道亮度,然后通过 gamma 曲线和色调响应曲线求出亮度对应的输入信号,从而将屏幕校准为 sRGB 。
通过校色 ICC 和校准目标 ICC 生成 MHC2 ICC 的工具已发布在 GitHub 上: https://github.com/dantmnf/MHC2
一开始我的直觉告诉我这个矩阵的输入是 RGB ,于是填了一个 [[0,1,0,0],[1,0,0,0],[0,0,1,0]]
试图交换红色分量和绿色分量,结果发现白点向紫色方向偏移了。后来看到 SDK 里那个 CscXyzMatrixRows
,又尝试在 XYZ 空间的白点中交换了 XY ,转换回 RGB 空间后,发现绿色分量变少了。
后来我在两边各乘了一个 XYZ/RGB 转换矩阵,成功互换了红色分量与绿色分量。但是我依然不能理解为什么一个声称 color space transform/conversion 的矩阵要应用在 XYZ 空间,尤其是变换结果依然当作 XYZ 解释。对此我直呼大脑升级
此处应有发光大脑 meme 图.jpg
在上面的方法中,Windows 依然认为设备色彩空间是 sRGB ,并且通过 GPU 的 display engine 将 framebuffer 中的 sRGB 信号转换为真正的设备色彩空间信号。这种做法不会占用额外的通用计算资源,但同时系统也无法输出在 sRGB 空间外,却在设备色彩空间内的颜色。
Windows 在引入 HDR 桌面的同时也带来了桌面混成的色彩管理(内部名称 advanced color):支持 advanced color 的应用输出 scRGB 信号,不支持 advanced color 的应用输出被认为是 sRGB 信号,各个应用的输出经混成器( dwm.exe )在 scRGB 空间进行混成后,最终转换为设备色彩空间信号传送到显示器。
这一过程对 HDR 桌面而言是必须的,但通过逆向分析 dxgkrnl.sys
,发现在特定条件下也可以在 SDR 桌面中强行启用,详见 https://github.com/dantmnf/MHC2#advanced-color-for-sdr 。通过对比多个版本的 dxgkrnl.sys
,推断这一功能可能会在 Windows 11 的下一个大版本更新中正式发布。
(Credit: @imbushuo)
如果显示器声称支持 HDR ,advanced color 将使用 Rec. 2020 (存疑) + PQ 作为设备色彩空间,由显示器内部转换为合适的面板信号。但对于消费级电子垃圾显示器而言,这个转换过程很可能是不准确且无法校准的。
不幸的是,在假 HDR* 显示器大行其道的今天,我们很难找到一台广色域但不支持 HDR 的显示器。如果你的显示器声明了假 HDR ,你可以尝试以下方法让 Windows 认为它不支持 HDR:
EDID_OVERRIDE
(具体效果与驱动有关,可能无法覆盖 HDR 信息)很可惜我是属于那种不能覆盖 HDR 信息并且没有专业显卡的,但是已经有两个有钱的群友在多个版本的 Windows 10/11 上测试成功了。
* 假 HDR ,又称 HDRn’t 、ScamHDR 400。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.