The PNG Guide is an eBook based on Greg Roelofs' book, originally published by O'Reilly. 
Home Reading PNG Images Gamma and Color Correction  
See also: Color Representation, RGB, Gamma and Color Correction, Gamma Correction and Precision Color, Chromaticity  
Gamma and Color CorrectionBut where color correction can be a little tricky, gamma correction is quite straightforward. All one needs is the ``gamma'' value (exponent) of the user's display system and that of the PNG file itself. If the PNG file does not include a gAMA or sRGB chunk, there is little to be done except perhaps ask the user for a bestguess value; a PNG decoder is likely to do more harm than good if it attempts to guess on its own. We will simply forego any attempt at gamma correction, in that case. But on the assumption that most PNG files will be well behaved and include gamma information, we included the following code at the beginning of the main program: double LUT_exponent; double CRT_exponent = 2.2; double default_display_exponent; #if defined(NeXT) LUT_exponent = 1.0 / 2.2; /* if (some_next_function_that_returns_gamma(&next_gamma)) LUT_exponent = 1.0 / next_gamma; */ #elif defined(sgi) LUT_exponent = 1.0 / 1.7; /* there doesn't seem to be any documented function to * get the "gamma" value, so we do it the hard way */ infile = fopen("/etc/config/system.glGammaVal", "r"); if (infile) { double sgi_gamma; fgets(fooline, 80, infile); fclose(infile); sgi_gamma = atof(fooline); if (sgi_gamma > 0.0) LUT_exponent = 1.0 / sgi_gamma; } #elif defined(Macintosh) LUT_exponent = 1.8 / 2.61; /* if (some_mac_function_that_returns_gamma(&mac_gamma)) LUT_exponent = mac_gamma / 2.61; */ #else LUT_exponent = 1.0; /* assume no LUT: most PCs */ #endif default_display_exponent = LUT_exponent * CRT_exponent; The goal here is to make a reasonably well informed guess as to the overall display system's exponent (``gamma''), which, as you'll recall from Chapter 10, "Gamma Correction and Precision Color", is the product of the lookup table's exponent and that of the monitor. Essentially all monitors have an exponent of 2.2, so I've assumed that throughout. And almost all PCs and many workstations forego the lookup table (LUT), effectively giving them a LUT exponent of 1.0; the result is that their overall displaysystem exponent is 2.2. This is reflected by the last line in the ifdef block. A few wellknown systems have LUT exponents quite different from 1.0. The most extreme of these is the NeXT cube (and subsequent noncubic models), which has a lookup table with a 1/2.2 exponent, resulting in an overall exponent of 1.0 (i.e., it has a ``linear transfer function''). Although some thirdparty utilities can modify the lookup table (with a ``gamma'' value whose inverse is the LUT exponent, as on SGI systems), there appears to be no system facility to do so and no portable method of determining what value a thirdparty panel might have loaded. So we assume 1.0 in all cases when the NeXTspecific macro NeXT is defined. Silicon Graphics workstations and Macintoshes also have nonidentity lookup tables, but in both cases the LUT exponent can be varied by system utilities. Unfortunately, in both cases the value is varied via a parameter called ``gamma'' that matches neither the LUT exponent nor the other system's usage. On SGI machines, the ``gamma'' value is the inverse of the LUT exponent (as on the NeXT) and can be obtained either via a command (gamma) or from a system configuration file (/etc/config/system.glGammaVal); there is no documented method to retrieve the value directly via a system function call. Here we have used the filebased method. If we read it successfully, the overall system exponent is calculated accordingly; if not, we assume the default value used on factoryshipped SGI systems: ``gamma'' of 1.7, which implies a displaysystem exponent of 2.2/1.7, or 1.3. Note, however, that what is being determined is the exponent of the console attached to the system running the program, not necessarily that of the actual display. That is, X programs can display on remote systems, and the exponent of the remote display system might be anything. One could attempt to determine whether the display is local by checking the DISPLAY environment variable, but to do so correctly could involve several system calls (uname(), gethostbyname(), etc.) and is beyond the scope of this demo program. A userlevel workaround is to set the SCREEN_GAMMA variable appropriately; I'll describe that in just a moment. The Macintosh ``gamma'' value is proportional to the LUT exponent, but it is multiplied by an additional constant factor of 2.61. The default gamma is 1.8, leading to an overall exponent of (1.8/2.61) × 2.2, or 1.5. Since neither of the two front ends (X or Windows) is designed to work on a Mac, the code inside the Macintosh ifdef (and the Macintosh macro itself) is intended for illustration only, not as a serious example of readytocompile code. Indeed, a standard component of Mac OS 8.5 is Apple's ColorSync color management system (also available as an addon for earlier systems), which is the recommended way to handle both gamma and color correction on Macs. It is entirely possible that the user has calibrated the display system more precisely than is reflected in the preceding code, or perhaps has a system unlike any of the ones we have described. The main program also gives the user the option of specifying the display system's exponent directly, either with an environment variable (SCREEN_GAMMA is suggested by the libpng documentation) or by direct input. For the latter, we have once again resorted to the simple expedient of a commandline option, but a more elegant program might pop up a dialog box of some sort, or even provide a calibration screen. In any case, our main program first checks for the environment variable: if ((p = getenv("SCREEN_GAMMA")) != NULL) display_exponent = atof(p); else display_exponent = default_display_exponent; If the variable is found, it is used; otherwise, the previously calculated default exponent is used. Then the program processes the commandline options and, if the gamma option is found, its argument replaces all previously obtained values. That turned out to be a moderately lengthy explanation of the demo program's approach to gamma correction (or, more specifically, to finding the correct value for the display system's exponent), mostly because of all the different ways the value can be found: systemspecific educated guesses at the time of compilation, systemspecific files or API calls at runtime, an environment variable, or direct user input. The actual code is only about 20 lines long.


Home Reading PNG Images Gamma and Color Correction 