Complete Source Code
Ordered Dithering
How is try.c accomplishing gamma correction?
try.c implements gamma correction by determining the corrected 8-bit intensity values for each of the 256 grayscale palette entries and then loading them into the X-windows ColorMap (the 256 color palette). The entire routine is accomplished in the following lines of code:
/* try very simple gamma correction */
for (i=0; i<256; i++)
{
int rgb = (int)(0.5 + 255*sqrt(i/255.0));
SetCmapEntry(X, i, rgb, rgb, rgb);
}
For each 8-bit intensity value entry in the ColorMap (palette), the intensity
is normalized, then the square root is taken to return a normalized corrected
intensity value, and finally the new 8-bit entry is determined by scaling
up the normalized corrected intensity and rounding to the nearest integer
(note that 0.5 had to be added before casting to an int since the cast
operation "chops" the decimal precision). This new 8-bit value is entered
into the ColorMap (palette) for the Red, Green, and Blue values (grayscale).
So how is the square root function performing gamma correction? Simple, it is approximating the natural intensity response of the monitor as the function f(x) = x^2 where f(x) is the output intensity and x is the applied voltage (all normalized of course). To photometrically linearize this display, the inverse of this function (the square root) was needed in order to correct the 8-bit input values so that a linear response could be attained.
Luminosity = InputVoltage^Gamma
CorrectedInputVoltage = InputVoltage^(1/Gamma)
CorrectedInputVoltage = sqrt(InputVoltage) = InputVoltage^(1/2)This means that the normalized gamma equation was used to photometrically linearize this display with gamma=2.
Of course, this was a quick and simple approximation since most gamma values for the natural response of monitors are near 2. So it was a fairly safe assumption to use gamma=2 and it fits nicely as a square root function rather than raising a number to a fractional power. Also, it didn't require any measuring of the monitor because the gamma was assumed to be "close enough".
How can the try.c approach to gamma correction be implemented differently?
We now know that the gamma value of 2 was chosen in order to be able to utilize the sqrt function to correct the 8-bit ColorMap entries. An alternate approach would be to simply evaluate the inverse of the gamma equation (assuming all normalized values):
CorrectInputVoltage = InputVoltage^(1/Gamma) = InputVoltage^(1/2)So instead of taking advantage of the fact that the sqrt is the same as raising a number to the 1/2 power, we can simply evaluate the inverse gamma equation. So, the new correction loop would look as follows:
/* try alternate form of gamma correction */
for (i=0; i<256; i++)
{
int rgb = (int)(0.5 + 255*pow((i/255.0),0.5) );
SetCmapEntry(X, i, rgb, rgb, rgb);
}