- From: <bugzilla@jessica.w3.org>
- Date: Thu, 07 Apr 2011 21:56:31 +0000
- To: public-html-bugzilla@w3.org
http://www.w3.org/Bugs/Public/show_bug.cgi?id=12444 Summary: Canvas rendering should be done in linear color space (gamma 1) and the result displayed in sRGB color space (approximately gamma 2.2) Product: HTML WG Version: unspecified Platform: All OS/Version: All Status: NEW Severity: normal Priority: P2 Component: HTML Canvas 2D Context (editor: Ian Hickson) AssignedTo: ian@hixie.ch ReportedBy: w3c@hugues.info QAContact: public-html-bugzilla@w3.org CC: mike@w3.org, public-html-wg-issue-tracking@w3.org, public-html@w3.org Created attachment 975 --> http://www.w3.org/Bugs/Public/attachment.cgi?id=975 This image shows a comparison of a series of images computed incorrectly (ignoring the gamma) and correctly (done in linear space). Canvas rendering should be done in linear color space (gamma 1) and the result displayed in sRGB color space (approximately gamma 2.2) Conventional CRT monitors have a non-linear response of intensity to input voltage. This response is characterized by a power function. The displayed intensity is the input value raised to a power of about 2.5. This 2.5 value is called the gamma of the monitor. The input value represents the color value sent by the graphics adapter to the monitor. It can be represented by a value ranging from 0 (that represents black) to 1 (that represents the full intensity, white, (255 in unsigned 8-bit terms)). Other computer monitors, like LCD monitors, also follow this gamma law. LCD monitors don’t have the same physical response as CRT monitors, but computer LCD monitors emulate the CRT response to display values similarly to CRT monitors. This gamma function completely changes how intensities (and thus colors) are displayed on the screen. All color values that are not exactly 0 or 1 (since applying a power function to these values leaves them unaffected) will be much darker than one might expect from their value. For example, a color of 0.5 will not be half as bright as 1, but actually 0.5^2.5 times, that is 0.177. This is much less than 0.5, almost 3 times darker. The color that is half as bright as white would be 0.5^(1 / 2.5), that is 0.758. Most users are completely unaware of this, and this is actually not a problem at all. When they pick a color using a color picker, they choose a color by how it is displayed, not by its numerical value, thus unwittingly compensating the gamma. Moreover, the human visual system is not linear but logarithmic. Because of this, a value will /feel/ for example half as bright when it is actually much darker. This is also why gamma is actually a good thing. Thanks to it, there is more data precision for darker values, to which the human eye is more sensitive. With only 8 bits of precision per color channel, it is not possible to store linear values without noticeable precision artifacts, while storing gamma-encoded values gives reasonable results. As long as the system only directly displays colors chosen by users on the monitor, the gamma is not a problem. Where things become more tricky, is when gamma-encoded color values are used in computations. When this happens, problems arise. The bad news is that the vast majority of software that manipulate color values is doing this. Yes. Averaging two color values by simply adding them and dividing the result by 2 will not give the correct result at all. For example, to compute the average of color values 0.01 and 0.79, simply doing 0.01 * 0.5 + 0.79 * 0.5, what would give 0.4, is incorrect. Indeed, the values must first be converted to their linear intensity by applying the gamma power. Then, the computation can be done. At the end, the result must be converted to be displayed on the screen, by applying the inverse of the gamma (this is called “gamma correction”). This gives the following computation: (0.01^2.5 * 0.5 + 0.79^2.5 * 0.5)^(1 / 2.5), what gives almost 0.6. That is a difference of 50%! It is thus essential to take gamma into account when making computations with color values to have a correct result displayed on the monitor. When drawing on canvas, a lot of such computations is done: blending colors together, drawing antialiased shapes or text, scaling images,… Let’s repeat this: antialiasing will not give good results if the gamma is not taken into account. Just open your favorite image editing software. Create a new image with a pure green background (0, 255, 0). Now select the most aggressive fuchsia (255, 0, 255). Draw antialiased text. Do you see dark edges around your characters? If you don’t, I would like to know the name of your software :). Now the good news is that because we are now aware of the situation, we can now handle things correctly. The Internet has chosen sRGB as its standard color space. This color space has been defined in such a way that the vast majority of computers, that use a conventional computer monitor and don’t do any color management, will comply (more or less) to the standard. sRGB defines an overall gamma of approximately 2.2. The reason it is 2.2 and not 2.5 is because the viewing conditions of sRGB assume a dimly lit environment (such as an office), which require a resulting gamma slightly above 1 for best results. The sRGB gamma is not exactly 2.2, though. The standard defines a transformation that approximates a gamma 2.2 curve. The following functions should be used: float sRGB_to_linear(float c) { if (c <= 0.04045) { return c / 12.92; } return pow((c + 0.055) / 1.055, 2.4); } float linear_to_sRGB(float c) { if (c <= 0.0031308) { return 12.92 * c; } return 1.055 * pow(c, 1 / 2.4) - 0.055; } To make computations with sRGB color values, the first step is to convert them all to linear with the first function. Then, the computation can be done (in a linear space, where all math works as expected :)). At the end, the result is converted from linear to sRGB with the second function and can be displayed. (The computations above done with a 2.5 gamma can easily be adapted to sRGB. The results will vary slightly, but the idea is the same. This is left as an exercise to the reader :).) The attachment shows a comparison of a series of images computed incorrectly (ignoring the gamma) and correctly (done in linear space). Now, about canvas. Because canvas can do all sort of interesting computations with colors, including alpha blending, antialiasing and image scaling, it should definitely do all those computations in a linear space. The best way to achieve this, is to store all canvas pixels in floating-point in linear space. All sRGB input colors (being a single value or pixels from an image) would be converted to linear space and all operations would be done in floating-point. At display, the resulting image would be converted to sRGB. The problem with this approach is that it has high memory requirements and would maybe not be suitable for embedded devices. Another possibility is to keep the canvas pixels stored in 8-bit sRGB (thus gamma-encoded), ready for display, but all operations that read from the canvas would receive color values converted on-the-fly to linear space. Like with the previous method, all computations would of course be done in linear space (preferably in floating-point), so any input sRGB color value (including image pixels) must be converted to linear, but this can of course be done one pixel at a time. When storing a pixel in the canvas, it would be converted on-the-fly to sRGB. Hardware acceleration already exists for this, as exposed by the GL_ARB_framebuffer_sRGB OpenGL extension (promoted to the core in OpenGL 3.0). The GL_EXT_texture_sRGB extension provides on-the-fly sRGB-to-linear color conversion and linear filtering for sRGB textures stored with 8 bits per color channel. Functions giving access to pixel values (reading and writing) should exist in two variants: one using 8-bit sRGB values, and one using floating-point (or 16-bit integer) linear values. With the above features, canvas would ensure the best possible rendering results, giving the web a powerful high-quality graphical component (even better than what most stand-alone applications frameworks are providing). Here are some references on the subject. Wikipedia - sRGB http://en.wikipedia.org/wiki/SRGB GPU Gems 3 The Importance of Being Linear http://http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html Gamma Correction in Computer Graphics http://www.teamten.com/lawrence/graphics/gamma/ Gamma error in picture scaling http://www.4p8.com/eric.brasseur/gamma.html John Hable - Uncharted 2: HDR Lighting http://darkchris.ath.cx/papers/Uncharted%202%20-%20HDR%20Lighting.pdf Gamma FAQ http://www.poynton.com/GammaFAQ.html Frequently-questioned answers about gamma - Gamma FQA http://www.poynton.com/notes/color/GammaFQA.html Wikipedia - Gamma correction http://en.wikipedia.org/wiki/Gamma_correction Simon's Graphics Blog Gamma-Correct Rendering http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/ What's wrong with 8-bit linear RGB? (or, gamma is a feature, not a bug!) http://tulrich.com/webgl/rgb/linear_vs_srgb.html Martin Breidt - Be gamma correct! http://scripts.breidt.net/tutorials.html#gamma PNG Specification - Gamma Tutorial http://www.libpng.org/pub/png/spec/1.2/PNG-GammaAppendix.html Why use sRGB to store images? http://mysite.verizon.net/spitzak/conversion/whysrgb.html Yet Another Gamma Correction Page http://www.graphics.cornell.edu/~westin/gamma/gamma.html Hugues De Keyzer -- Configure bugmail: http://www.w3.org/Bugs/Public/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the QA contact for the bug.
Received on Thursday, 7 April 2011 21:56:35 UTC