12

我想添加在应用程序启动时调整屏幕伽玛并在退出时重置它的功能。虽然是否应该篡改 gamma 是有争议的(我个人认为它无用且有害),但是嘿,有些人希望能够做那种事情。

这也只是一个简单的 API 调用,所以一切都很简单,对吧?

MSDN 说:“伽马斜坡在 256 个 WORD 元素的三个数组中指定,每个 [...] 值必须存储在每个 WORD 的最高有效位中,以增加 DAC 的独立性。” . 这意味着,在我的理解中,类似于word_value = byte_value<<8,这听起来很奇怪,但这是我阅读它的方式。

Doom3 源代码包含一个函数,该函数接受三个char值数组并将它们转换为一个在上半部和下半部uint16_t具有相同字节值的值数组。换句话说,类似word_value = (byte_value<<8)|byte_value. 这同样很奇怪,但更糟糕的是它与上面的不一样

互联网上的各种业余程序员网站上也存在一些代码片段(显然一个是从另一个窃取的,因为它们与字母相同),它们做了一些模糊的数学运算,将线性索引乘以一个值,偏置为 128,并夹紧到 65535。我不太确定这是怎么回事,但对我来说这完全是胡说八道,而且它与上述两者都不相同。

是什么赋予了?它必须是明确定义的——无需猜测——您提供的数据必须是什么样子?最后,要做的是读取原始值并让用户无论如何调整一些滑块(并可选择使用用户的配置将该 blob 保存到磁盘),但仍然......为了修改这些值,需要知道它们是什么以及期望什么。

有没有人做过(并测试过!)这个并且知道哪个是正确的?

4

2 回答 2

11

在研究以编程方式更改屏幕亮度的能力时,我遇到了这篇文章以编程方式更改屏幕亮度 - 通过使用 Gama Ramp API

使用调试器,我查看了函数提供的值GetDeviceGamaRamp()。输出是一个定义为类似的二维数组WORD GammaArray[3][256];,是一个包含 256 个值的表,用于修改显示像素的红色、绿色和蓝色值。我看到的值从索引 0 处的零 (0) 值开始,然后加上一个值 256 来计算下一个值。因此,对于索引 0、1、2、...、254、255,序列为 0、256、512、...、65024、65280。

我的理解是这些值用于修改每个像素的 RGB 值。通过修改表格值可以修改显示亮度。但是,此技术的有效性可能因显示硬件而异。

您可能会发现这篇简短的文章Gamma Controls很有趣,因为它从 Direct3D 的角度描述了 Gamma 斜坡级别。这篇文章有关于 Gamma Ramp Levels 的内容。

在 Direct3D 中,术语 gamma ramp 描述了一组值,这些值将帧缓冲区中所有像素的特定颜色分量(红色、绿色、蓝色)的级别映射到 DAC 接收的用于显示的新级别。重新映射是通过三个查找表来执行的,每个颜色分量一个。

下面是它的工作原理: Direct3D 从帧缓冲区中获取一个像素,并计算其单独的红色、绿色和蓝色分量。每个组件由一个从 0 到 65535 的值表示。Direct3D 采用原始值并使用它来索引一个 256 元素数组(斜坡),其中每个元素都包含一个替换原始值的值。Direct3D 对帧缓冲区中每个像素的每个颜色分量执行此查找和替换过程,从而更改所有屏幕像素的最终颜色。

根据在线文档,GetDeviceGamaRamp()SetDeviceGamaRamp()Windows 2000 Professional 开始的 Windows API 支持这些功能。

我将他们的源代码浓缩为插入到 Windows 应用程序中的以下示例,以使用参考文章中的值测试效果。我的测试是使用 Windows 7 和 AMD Radeon HD 7450 图形适配器完成的。

通过这个测试,我的两个显示器,我有两个显示器,都受到了影响。

//Generate the 256-colors array for the specified wBrightness value.
WORD  GammaArray[3][256];
HDC   hGammaDC = ::GetDC(NULL);
WORD  wBrightness;

::GetDeviceGammaRamp (hGammaDC, GammaArray);

wBrightness = 64;     // reduce the brightness
for (int ik = 0; ik < 256; ik++) {
    int iArrayValue = ik * (wBrightness + 128);
    if (iArrayValue > 0xffff) iArrayValue = 0xffff;
    GammaArray[0][ik] = (WORD)iArrayValue;
    GammaArray[1][ik] = (WORD)iArrayValue;
    GammaArray[2][ik] = (WORD)iArrayValue;
}

::SetDeviceGammaRamp (hGammaDC, GammaArray);
Sleep (3000);

wBrightness = 128;    // set the brightness back to normal
for (int ik = 0; ik < 256; ik++) {
    int iArrayValue = ik * (wBrightness + 128);
    if (iArrayValue > 0xffff) iArrayValue = 0xffff;
    GammaArray[0][ik] = (WORD)iArrayValue;
    GammaArray[1][ik] = (WORD)iArrayValue;
    GammaArray[2][ik] = (WORD)iArrayValue;
}

::SetDeviceGammaRamp (hGammaDC, GammaArray);
Sleep (3000);

::ReleaseDC(NULL, hGammaDC);

作为附加说明,我对上面的源代码做了一点改动,因此我没有对每个 RGB 值进行同等修改,而是注释掉了前两个分配,因此只GammaArray[2][ik]进行了修改。结果是显示器偏黄。

我还尝试将上述源代码放在一个循环中以检查显示的变化情况,这与wBrightness=0to有很大的不同wBrightness=128

for (wBrightness = 0; wBrightness <= 128; wBrightness += 16) {
    for (int ik = 0; ik < 256; ik++) {
        int iArrayValue = ik * (wBrightness + 128);
        if (iArrayValue > 0xffff) iArrayValue = 0xffff;
        GammaArray[0][ik] = (WORD)iArrayValue;
        GammaArray[1][ik] = (WORD)iArrayValue;
        GammaArray[2][ik] = (WORD)iArrayValue;
    }

    ::SetDeviceGammaRamp (hGammaDC, GammaArray);
    Sleep (3000);
}

Microsoft 提供了在线 MSDN 文章Using gamma correction,它是 Direct3D 文档的一部分,它描述了 gamma 的基础知识,如下所示:

在图形管道的末端,也就是图像离开计算机沿显示器电缆传输的地方,有一小块硬件可以动态转换像素值。该硬件通常使用查找表来转换像素。该硬件使用来自要显示的表面的红色、绿色和蓝色值来查找表中的伽马校正值,然后将校正值而不是实际表面值发送到监视器。所以,这个查找表是一个用任何其他颜色替换任何颜色的机会。虽然该表具有该级别的功能,但典型的用途是巧妙地调整图像以补偿显示器响应的差异。监视器的响应是与红色数值相关的函数,

此外,软件应用程序 Redshift 有一个页面 Windows 伽玛调整,其中包含有关 Microsoft Windows 的信息。

在将 Redshift 移植到 Windows 时,我在设置低于 4500K 的色温时遇到了麻烦。问题在于 Windows 对可以进行哪些类型的 gamma 调整设置了限制,这可能是为了保护用户免受恶意程序的影响,这些程序会反转颜色、使显示器空白或使用 gamma 斜坡玩一些其他烦人的技巧。这种限制也许是可以理解的,但问题是完全缺乏此功能的文档(MSDN 上的 SetDeviceGammaRamp)。尝试设置不允许的伽马斜坡的程序将简单地失败并出现一般错误,让程序员想知道出了什么问题。

于 2015-10-21T20:33:31.500 回答
2

我没有对此进行测试,但如果我不得不猜测的话,早期的显卡在编写 Doom 时在 SetDeviceGammaRamp() 的实现中是非标准的,有时使用 LOBYTE,有时使用 WORD 值的 HIBYTE。共识转移到仅使用 HIBYTE,因此word_value = byte_value<<8.

这是另一个数据点,来自 PsychoPy 库(在 python 中),它只是交换 LOBYTE 和 HIBYTE:

 """Sets the hardware look-up table, using platform-specific ctypes functions. 
 For use with pyglet windows only (pygame has its own routines for this). 
 Ramp should be provided as 3x256 or 3x1024 array in range 0:1.0 
 """ 
if sys.platform=='win32':   
    newRamp= (255*newRamp).astype(numpy.uint16) 
    newRamp.byteswap(True)#necessary, according to pyglet post from Martin Spacek 
    success = windll.gdi32.SetDeviceGammaRamp(pygletWindow._dc, newRamp.ctypes) 
    if not success: raise AssertionError, 'SetDeviceGammaRamp failed' 

Windows似乎也不允许所有伽玛设置,请参阅:http: //jonls.dk/2010/09/windows-gamma-adjustments/

更新:

第一个提供伽玛控制的 Windows API 是 Windows 图形设备接口 (GDI) 的 SetDeviceGammaRamp 和 GetDeviceGammaRamp。这些 API 使用三个 256 项的 WORD 数组,每个 WORD 编码从 0 到 1,由 WORD 值 0 和 65535 表示。 WORD 的额外精度通常在实际的硬件查找表中不可用,但这些 API 是旨在灵活。与本节后面描述的其他 API 相比,这些 API 只允许与恒等函数有很小的偏差。事实上,斜坡中的任何条目都必须在标识值的 32768 范围内。此限制意味着任何应用程序都不能将显示屏完全变黑或变成其他不可读的颜色。

http://msdn.microsoft.com/en-us/library/windows/desktop/jj635732(v=vs.85).aspx

于 2014-01-22T22:31:52.897 回答