正确使用SetDeviceGammaRamp

时间:2011-12-02 16:41:38

标签: winapi gamma

我想在应用程序启动时添加调整屏幕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保存到磁盘),但仍然......为了修改这些值,需要知道它们是什么以及它们的预期。

之前有没有人做过(并经过测试!),知道哪一个是对的?

2 个答案:

答案 0 :(得分:7)

在调查以编程方式更改屏幕亮度的能力时,我发现了这篇文章Changing the screen brightness programmingly - By using the 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 Ramp Levels的说法。

  

在Direct3D中,术语伽马斜坡描述了一组映射的值   特定颜色成分的水平 - 红色,绿色,蓝色 - 适合所有人   帧缓冲区中的像素到DAC接收的新电平   用于显示。通过三次查找执行重新映射   表格,每个颜色组件一个。

     

以下是它的工作原理:Direct3D从帧缓冲区获取一个像素   评估其各自的红色,绿色和蓝色成分。每   component由0到65535之间的值表示.Direct3D占用   原始值并使用它来索引256个元素的数组(   ramp),其中每个元素都包含一个替换原始元素的值   一。 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=0wBrightness=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的基本知识,如下所示:

  

在图形管道的末尾,只是图像离开的位置   电脑沿着显示器电缆走,有一个小的   可以动态转换像素值的硬件。这个   硬件通常使用查找表来转换像素。这个   硬件使用来自的红色,绿色和蓝色值   要显示的表面以查找表中的伽马校正值   然后将更正的值发送到监视器而不是   实际表面值。所以,这个查找表是一个机会   用任何其他颜色替换任何颜色。虽然表有这个级别   在功率方面,典型的用法是巧妙地调整图像以进行补偿   对于显示器响应的差异。显示器的响应是   关联红色,绿色和红色数值的函数   具有该像素显示亮度的像素的蓝色分量。

此外还有关于Microsoft Windows的software application Redshift has a page Windows gamma adjustments

  

将Redshift移植到Windows时,我在设置时遇到了麻烦   色温低于约4500K。问题是Windows   对可以进行何种伽玛调整设置限制,   可能作为一种保护用户免受邪恶程序攻击的手段   反转颜色,使显示屏空白或播放其他烦人的东西   伽马斜坡的技巧。或许这种限制   可以理解,但问题是完全缺乏文档   此功能(SetDeviceGammaRamp on MSDN)。试图尝试的程序   设置一个不允许的伽马斜坡只会失败一般   错误让程序员想知道出了什么问题。

答案 1 :(得分: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/

<强>更新

  

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

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