For a better understanding of gamma correction I suggest reading this wikipedia entry:
This article does not cover the basics of Windows GDI nor X11. If you're unsure about the basics for your target platform, you should familiarize yourself with that platform before proceeding.
For the systems discussed here it all boils down to similar operations. We usually have three arrays (red, green, blue) of 256 color values, called a gamma ramp, which we get from the graphics device or set to the graphics device.
While we can adjust values for individual colors, we'll usually use a single factor, which in this article ranges from -1.0(dark) to 1.0(bright), where 0.0 is normal(default) value. However, while the system may have routines which also use a factor (instead of arrays), the range may be different. This is the case for X11, where the range is from 0.100 to 10.0 (1.0 being normal). Windows does not provide routines which use a factor, only ramps.
It's important to store the original values, before we change anything, so that when our program finishes we can restore these values. This helps not to disturb whatever settings the user has for his desktop or graphics device. In this case you'll want to store the entire gamma ramp as the way we calculate gamma may differ from the system (or device).
The type of structure we'll be using for both platforms is the same. We have 3 arrays, each of 256 word elements. This can be represented as a single multidimensional array. Where array 0 is red, 1 green, and 2 blue.
TYPE TGammaRamp = array[0..2, 0..255] of word;
According to MSDN the functionality is available since Windows 2000. I am unsure if this functionality is available in earlier Windows operating systems.
The Windows functions we use mostly accept a pointer to a gamma ramp structure. Depending on your Pascal compiler, this may be passed on as a var parameter (hence, no pointer needed). Such is the case for Free Pascal.
To set or get the gamma ramp we need to specify a device context handle to the Windows functions we use. The easiest way to obtain the device context handle is to use the GetDC() windows function, and specify 0 or NULL as the window handle (which returns the device context handle for the desktop).
var dc: HDC; begin dc := GetHDC(0); end;
The structure where we store our values is consisted of three arrays, each having 256 word values. The arrays are for red, green and blue colors respectively. The structure is described in
3.3 Setting the gamma ramp
To set the gamma ramp, we use the SetDeviceGammaRamp() routine which takes a device context handle, and a pointer to the gamma ramp array (structure described in 3.1).
To get the gamma ramp, we use the GetDeviceGammaRamp() routine which takes a device context handle, and a pointer to the gamma ramp array (structure described in 3.1).
Under X11, for each function we call, we'll need to have a display opened. Here we'll refer to it as dpy.
A simple way to open the default display is to call the XOpenDisplay()function with a nil pointer as the parameter. This will return a pointer to a display.
var dpy: PDisplay = nil; begin dpy := XOpenDisplay(nil); end;
screen := DefaultScreen(dpy);
We need to check if the extension is available calling the XF86VidModeQueryExtension()function. This function will return a non-zero value if the extension is present. If it returns 0 then the extension is not available, and we cannot perform any gamma functionality.
var event_base, error_base: longint; begin if(XF86VidModeQueryExtension(lnxDPY, @event_base, @error_base) <> 0) then writeln('We have xf86vmode'); end;
Before the gamma set and get routines have any effect, we'll need to process(get) events from X11. You can use the XNextEvent() routine to achieve this. Since this is a standard part of any X11 application I will not go into detail on X11 event processing.
All routines return a non-zero value (usually 1) on success, and 0 if they fail. In C any non-zero value means true and 0 means false, and X11 is C based.
There is no specific structure, as the functions expect a pointer to arrays of dword. However, three are always three arrays. One for red, green and blue. Though the functions accept also a size(longint) parameter you can safely assume that the size of the arrays is 256. Perhaps this might change when we go to 48-bit colors. Throughout the article we'll use the structure as described in section 2.2.
You can always get the size of the gamma ramp by using XF86VidModeGetGammaRampSize(). To store arrays of different sizes will require you to use dynamic arrays.
XF86VidModeGetGammaRampSize(dpy, 0, @size);
for idx := 0 to 255 do begin v := round(idx*((gamma*0.5+0.5)*255+128)); if(v> 65535) then v := 65535; ramp[idx] := word(v); ramp[idx] := word(v);
To get the gamma ramp we'll use the XF86VidModeGetGammaRamp() function, which takes the display, screen, ramp size and pointers to red, green and blue arrays to which the values will be stored.
XF86VidModeGetGammaRamp(dpy, screen, size, @ramp, @ramp, @ramp);
4.4 Setting the gamma ramp
To get the gamma ramp we'll use the XF86VidModeSetGammaRamp() function. which takes the display, screen, ramp size and pointers to red, green and blue arrays from which the values will be read.
XF86VidModeSetGammaRamp(dpy, screen, size, @ramp, @ramp, @ramp);
4.5 Alternative way
XF86VidMode also provides an alternative, simpler, way to set or get the gamma values. This is what I use in my own code.
First, the structure used contains three single precision floating point values for red, green and blue color.
TYPE TGamma = record red, green, blue: single; end;
To get the gamma values use the XF86VidModeGetGamma() routine, and to set use XF86VidModeSetGamma(). Both functions take a display, a screen and a pointer to the gamma structure as parameters.
XF86VidModeGetGamma(dpy, screen, @gamma); XF86VidModeSetGamma(dpy, screen, @gamma);
5. Calculating gamma correction values
Okay, we have a gamma correction factor (-1.0 to 1.0), but how to calculate the individual values for the gamma ramp structure? The maximum value that any color can have is 65535(bright), and lowest can be 0(dark). The normal value is in the middle (32767). If we have a factor of 1.0, then we want to have the brightest values, in case we have -1.0 then we want to have darkest values.
Here is a sample code how to calculate the values. idx is the color index 0..255, v the word value we calculate, gamma is the gamma correction factor and ramp is the gamma ramp structure described in section 2.2.
for idx := 0 to 255 do begin v := round(idx*((gamma*0.5+0.5)*255+128)); if(v> 65535) then v := 65535; ramp[idx] := word(v); ramp[idx] := word(v); ramp[idx] := word(v); end;
Provided in the attachments are demo programs for both X11 and Windows. They are small and demonstrate the gamma functionality in the simplest possible way.
Last Update: 21.10.2010.
by Dejan Boras (de_jean_7777)
by Dejan Boras (de_jean_7777)