Update! Alpha Correction can be used to simulate sRGB blending without sRGB blending primitive.
To view this demo, you need TFT display with RGB subpixel order. It works in Firefox and Chrome, and maybe in some other compatible browser. Here's Linux and Windows 7 side-by-side, if you can't see the demo live. OS X font rendering results in all of the AppleFontSmoothing levels are discussed here. The problem I'm talking about is with the text in the bottom box that shows significant color fringing when Linux renders it. The rest of the page deals with why this happens and what can be done about it.
The technical problem is missing colorspace conversion during alpha blending. Libraries such as freetype generate coverage data of the glyphs quantized to 8 bits, so a value such as 0x80 from freetype means "50 % of this pixel is foreground and 50 % is background". That is to say: the visible light emitted by the pixel on screen should be a 50 % blend between the intensities of the colors as they would appear on screen surface unblended. However, RGB component values do not have a linear relationship with light intensities on the screen. In simplest possible terms, the average of white and black color (in physical light intensity terms) is not r=g=b=128 but r=g=b=188. To account for this nonlinearity, we must do gamma correction.
The simplest way to do the gamma correction is to mathematically approximate the component-to-light-intensity formula, which is approximately a power law, and then perform the blending, and then perform the power law in reverse, so that the display will then apply the power law again when it displays the color. However, multiple conversions like this are time-consuming, inconvenient and result in loss of accuracy. (Result is still more accurate than you would get from ignoring the gamma entirely, though!) From a programmer point of view, a sane thing to do would be to begin using linear color representations everywhere, and to spend more bits at the dark intensities. As it turns out, there's actually a specification that does just this, called scRGB(16).
In this demo, I am simply employing gamma correction between single-color foreground and background, where the foreground is the text color and the background is whatever the text is on. You can control the third example, where I have chosen the alphablending case of opposite colors that personally annoys me the most. Just set gamma = 1.0 in the control form to emulate typical Linux result, and see that missing gamma correction causes color fringing, excessive darkening and/or smudginess at curves.
#define GAMMA 2.2 /* input1 = source color 1, 0 .. 1 * input2 = source color 2, 0 .. 1 * alpha = alpha blend factor, 0 .. 1 */ // uncorrected formula (same as GAMMA=1.0): // double output = input1 * alpha + input2 * (1.0 - alpha); // corrected formula: double tmp = pow(input1, GAMMA) * alpha + pow(input2, GAMMA) * (1.0 - alpha); double output = pow(tmp, 1.0/GAMMA);
White colors tend to bleed into dark colors in the eye, resulting in correctly calculated inverse images appearing perceptively inequal. For instance, you might think that the white-on-black text is much stronger than the black-on-white text. If you are very suspectible to this effect, it unfortunately means that you can not fully trust what your eyes are telling you, but need to mentally compensate for this effect. (The code is still correct, even if the results might not subjectively please you.)
You can test your system's gamma here. Technically there is no single exponent value that converts between the sRGB and linear light color spaces, but the error caused with the 2.2 approximation is fairly small for this use case. Feel free to use official sRGB conversion formulas in production applications.
Bonus: an excellent demo about related problem that occurs with most image scaling software.