While investigating the ability to change screen brightness programmatically, I came across this article Changing the screen brightness programmingly - By using the Gama Ramp API.
Using the debugger, I took a look at the values provided by the GetDeviceGamaRamp()
function. The output is a two dimensional array defined as something like WORD GammaArray[3][256];
and is a table of 256 values to modify the Red, Green, and Blue values of displayed pixels. The values I saw started with a value of zero (0) at index 0 and adding a value of 256 to calculate the next value. So the sequence is 0, 256, 512, ..., 65024, 65280 for indices 0, 1, 2, ..., 254, 255.
My understanding is that these values are used to modify the RGB value for each pixel. By modifying the table value you can modify the display brightness. However the effectiveness of this technique may vary depending on display hardware.
You may find this brief article, Gamma Controls, of interest as it describes gamma ramp levels though from a Direct3D perspective. The article has this to say about Gamma Ramp Levels.
In Direct3D, the term gamma ramp describes a set of values that map
the level of a particular color component—red, green, blue—for all
pixels in the frame buffer to new levels that are received by the DAC
for display. The remapping is performed by way of three look-up
tables, one for each color component.
Here's how it works: Direct3D takes a pixel from the frame buffer and
evaluates its individual red, green, and blue color components. Each
component is represented by a value from 0 to 65535. Direct3D takes
the original value and uses it to index a 256-element array (the
ramp), where each element contains a value that replaces the original
one. Direct3D performs this look-up and replace process for each color
component of each pixel in the frame buffer, thereby changing the
final colors for all of the on-screen pixels.
According to the online documentation for GetDeviceGamaRamp()
and SetDeviceGamaRamp()
these functions are supported in the Windows API beginning with Windows 2000 Professional.
I used their source condensed down to the following example inserted into a Windows application to test the effect using values from the referenced article. My testing was done with Windows 7 and an AMD Radeon HD 7450 Graphics adapter.
With this test both of my displays, I have two displays, were affected.
//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);
As an additional note, I made a slight change to the above source so that instead of modifying each of the RGB values equally, I commented out the first two assignments so that only GammaArray[2][ik]
was modified. The result was a yellowish cast to the display.
I also tried putting the above source in a loop to check how the display changed and it was quite a difference from wBrightness=0
to 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 provides an on-line MSDN article, Using gamma correction, that is part of the Direct3D documentation which describes the basics of gamma as follows:
At the end of the graphics pipeline, just where the image leaves the
computer to make its journey along the monitor cable, there is a small
piece of hardware that can transform pixel values on the fly. This
hardware typically uses a lookup table to transform the pixels. This
hardware uses the red, green and blue values that come from the
surface to be displayed to look up gamma-corrected values in the table
and then sends the corrected values to the monitor instead of the
actual surface values. So, this lookup table is an opportunity to
replace any color with any other color. While the table has that level
of power, the typical usage is to tweak images subtly to compensate
for differences in the monitor’s response. The monitor’s response is
the function that relates the numerical value of the red, green and
blue components of a pixel with that pixel’s displayed brightness.
Additionally the software application Redshift has a page Windows gamma adjustments which has this to say about Microsoft Windows.
When porting Redshift to Windows I ran into trouble when setting a
color temperature lower than about 4500K. The problem is that Windows
sets limitations on what kinds of gamma adjustments can be made,
probably as a means of protecting the user against evil programs that
invert the colors, blank the display, or play some other annoying
trick with the gamma ramps. This kind of limitation is perhaps
understandable, but the problem is the complete lack of documentation
of this feature (SetDeviceGammaRamp on MSDN). A program that tries to
set a gamma ramp that is not allowed will simply fail with a generic
error leaving the programmer wondering what went wrong.