What's wrong with 8-bit linear RGB?

or, gamma is a feature, not a bug!

There is a persistent internet myth that the non-linear gamma (e.g. a power of 2.2) applied to electronic image data (like TV signals or RGB images) is due to the nonlinear light response of the phosphors in a CRT display. I.e. that gamma is a compensation for a defect in old-fashioned display technology.

This is a myth! Gamma is not a defect! The scientists and engineers who designed TV were incredibly smart; definitely smarter than you and me and anyone we know. They did not botch this. Gamma is a feature designed to minimize visible noise and distortion when representing and transmitting images electronically. The principle applies equally in the analaog and digital domains.

This FQA explains all. We can verify it for ourselves by doing experiments, like the one below.

For game developers interested in gamma and lighting and how to make them coexist peacefully, these slides are also good reading.

Ordinary JPG image as shown by the browser. JPEGs are almost universally stored with gamma ~2.2; after decompression, browsers pass the data through to video subsystems, and video subsystems and displays invert the gamma to produce the image you see.

Same image drawn onto a 2D <canvas>, mapped to linear 888 and then back to sRGB for display in 2D canvas.

At first glance, they look the same.

Now compare the dark areas, like on the dog's back, the window frame, the bunched up comforter in the foreground, etc. She's still a cute dog, but somebody pixellated the blacks! It looks like the video in the game Seventh Guest from 1993!

The loss of data in the darker areas is not the fault of the second conversion (back to sRGB from linear). It is a consequence of quantizing 8-bit data in a linear space. Once you do that, the information is gone forever.

In numbers, suppose you have a red component in sRGB space, with the value 12. Converting to linear, you do pow(12 / 255, 2.2) * 255 ~= 0.306. To store that as an 8-bit integer you have to round off or truncate the fraction, leaving you with 0. It doesn't matter what conversion you do now; the distinction between sRGB==12 and sRGB==0 is gone forever.