This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 12444 - Canvas rendering should be done in linear color space (gamma 1) and the result displayed in sRGB color space (approximately gamma 2.2)
Summary: Canvas rendering should be done in linear color space (gamma 1) and the resul...
Status: RESOLVED WONTFIX
Alias: None
Product: HTML WG
Classification: Unclassified
Component: LC1 HTML Canvas 2D Context (show other bugs)
Version: unspecified
Hardware: All All
: P2 normal
Target Milestone: ---
Assignee: contributor
QA Contact: HTML WG Bugzilla archive list
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-04-07 21:56 UTC by Hugues De Keyzer
Modified: 2011-09-21 17:54 UTC (History)
7 users (show)

See Also:


Attachments
This image shows a comparison of a series of images computed incorrectly (ignoring the gamma) and correctly (done in linear space). (396.87 KB, image/png)
2011-04-07 21:56 UTC, Hugues De Keyzer
Details

Description Hugues De Keyzer 2011-04-07 21:56:29 UTC
Created attachment 975 [details]
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 dont 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,

Lets 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 dont, 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 dont 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
Comment 1 Frank Olivier 2011-04-07 22:52:32 UTC
Please see http://lists.w3.org/Archives/Public/public-html/2010Mar/0144.html
for a discussion on HTML5 and color management. 
Also,
http://lists.w3.org/Archives/Public/public-canvas-api/2009OctDec/0001.html for
a discussion on color management and drawing images to canvas.

WRT "and the result displayed in sRGB color space (approximately gamma 2.2)"
from the bug title:

Based on data we collected some time ago, sRGB set as the display profile on
~70% of Windows PCs; the other 30% have a (non-sRGB) monitor-appropriate
profile assigned via Windows Update (most common) or the user calibration (less
common)

Wrt
http://www.w3.org/TR/html5/the-canvas-element.html#color-spaces-and-color-correction
It would be more useful to define a working color space for canvas elements to
clarify canvas element color management.
Comment 2 Hugues De Keyzer 2011-04-08 17:45:57 UTC
(In reply to comment #1)
> Please see http://lists.w3.org/Archives/Public/public-html/2010Mar/0144.html
> for a discussion on HTML5 and color management. 
> Also,
> http://lists.w3.org/Archives/Public/public-canvas-api/2009OctDec/0001.html for
> a discussion on color management and drawing images to canvas.

Hi,

Thanks for your reply.

Please note that the subject explained here indeed relates to color management, but it also differs highly from what color management usually implies. As far as I know, the goal of color management is to precisely define colors and ensure that colors are correctly interpreted, displayed and transfered from one device to the other. It will define how to convert from one color profile to another, but doesnt define how colors must be produced or interpreted as part of rendering computations. This is not only about interpreting and displaying colors, but more importantly, about how to correctly create them.

The subject of using a linear space for rendering is rather new (less than 10 years), is relatively hard to understand well, and is spreading slowly because of this. Once it is understood, it becomes obvious that this is clearly the way to go. It is now relatively well known and used by the film industry, and little by little also by the video game industry.

> WRT "and the result displayed in sRGB color space (approximately gamma 2.2)"
> from the bug title:
> 
> Based on data we collected some time ago, sRGB set as the display profile on
> ~70% of Windows PCs; the other 30% have a (non-sRGB) monitor-appropriate
> profile assigned via Windows Update (most common) or the user calibration (less
> common)

The title should have been more correctly stated as  and the result output in sRGB color space. sRGB being the standard color space of the Internet, the resulting image should use the same color space as other colors on the page, like those used in CSS. If the computer display is using another color profile, the colors should be converted to that profile for display, like the other colors of the page (possibly in one conversion from linear to that color space).

> Wrt
> http://www.w3.org/TR/html5/the-canvas-element.html#color-spaces-and-color-correction
> It would be more useful to define a working color space for canvas elements to
> clarify canvas element color management.

What do you mean by working color space? If it is the color space in which the pixels are stored internally, I would advise either gamma-encoded sRGB in 8-bit or linear color space in floating-point (with the same primaries as sRGB, but with a gamma of 1). In either case, all rendering operations should be done in linear space, preferably in floating-point.
Comment 3 Frank Olivier 2011-04-11 19:27:51 UTC
> Please note that the subject explained here indeed relates to color management,
> but it also differs highly from what color management usually implies. As far
> as I know, the goal of color management is to precisely define colors and
> ensure that colors are correctly interpreted, displayed and transfered from one
> device to the other. It will define how to convert from one color profile to
> another, but doesnt define how colors must be produced or interpreted as part
> of rendering computations. This is not only about interpreting and displaying
> colors, but more importantly, about how to correctly create them.
The change you are asking for has color management implications, which is why a discussion of the implicit color space of a canvas element is relevant.

> The title should have been more correctly stated as  and the result output in
> sRGB color space. sRGB being the standard color space of the Internet, the
> resulting image should use the same color space as other colors on the page,
> like those used in CSS. If the computer display is using another color profile,
> the colors should be converted to that profile for display, like the other
> colors of the page (possibly in one conversion from linear to that color
> space).
It seems odd to ask for a floating point linear gamma representation of canvas pixels - only to output the result via sRGB? Why the arbitrary clamp?

> What do you mean by working color space?
The implicit color space of a canvas element.

> If it is the color space in which the pixels are stored internally...
Correct

> I would advise either gamma-encoded sRGB in 8-bit
AFAIK this is how all user agents treat canvas today.

> or linear color space in floating-point (with the same primaries as sRGB,
> but with a gamma of 1).
This would a large architectural change for user agents; canvas elements would use a lot more memory and render slower.

> In either case, all rendering operations should be done
> in linear space, preferably in floating-point.
The author can do this with sRGB-to-linear conversions, if the color space of the canvas is known or assumed to be sRGB - IOW authors do create linear gamma drawing operations by calculating the 'correct' color values themselves.
Comment 4 Hugues De Keyzer 2011-04-14 20:12:57 UTC
(In reply to comment #3)
> The change you are asking for has color management implications, which is why a
> discussion of the implicit color space of a canvas element is relevant.

Yes, of course. I just wanted to point out that it is not limited to color management.

> It seems odd to ask for a floating point linear gamma representation of canvas
> pixels - only to output the result via sRGB? Why the arbitrary clamp?

A linear floating point color data representation represents the best possibility in terms of quality. This is what is used thoroughly now in computer graphics. For example, one of the most used file formats to store rendered images is OpenEXR, which stores pixel data in 16-bit floating point. Most high-end video games do all their rendering in floating-point off-screen textures that are composited and post-processed before being displayed in 8-bit sRGB.

Storing image data in floating-point has actually two advantages. The first advantage is precision. Because floating-point numbers have a precision that is relative to their value, they are very well suited for storing logarithmic values, and light intensity is perceived as logarithmic by the human visual system. The precision advantage is why I am recommending floating-point here (if possible).

The second advantage is the possible range of values. Floating-point numbers are not limited to a range from 0 to 1 (or 0 to 255). They can be thousands of times higher than 1, and even negative. Now, what is the point of such values, when displayed values will anyway be between 0 and 1? Indeed, the final result will have values between 0 and 1, but because image pixels are used in computations (blending, etc.), such values are important as intermediate values. With only values clamped between 0 and 1, (1 + 1) / 2 would be equal to 0.5 instead of 1, because no intermediate result higher than 1 can be stored. This can give ugly results. Fading out a picture containing out-of-range highlights (such as a picture of a landscape where the sun is visible) will make these highlights appear gray, while they should stay bright white. Take a look at the 4th OpenEXR sample picture (a desk in front of tainted glass) on the bottom of this page: http://www.openexr.com/samples.html. It represents the same image where values are scaled down. This would be impossible to do with values clamped between 0 and 1. These high dynamic range features are actually out of the scope of the subject of my first message, but if we really want the best possible results, they should be considered.

> This would a large architectural change for user agents; canvas elements would
> use a lot more memory and render slower.

Yes, indeed. That is why I propose the alternative method where conversions are done on-the-fly. This would require less memory but would be actually probably more CPU-intensive. Lookup tables can possibly be used to accelerate these operations. Ideally, this should be implemented in hardware.

> > In either case, all rendering operations should be done
> > in linear space, preferably in floating-point.
> The author can do this with sRGB-to-linear conversions, if the color space of
> the canvas is known or assumed to be sRGB - IOW authors do create linear gamma
> drawing operations by calculating the 'correct' color values themselves.

Indeed, some computations can be done manually. What I propose is that the results would be good by default. Most programmers are unaware of these gamma problems. Currently, simply drawing a circle or text gives wrong antialiasing (try with fuchsia on a green background, you get dark edges). Same thing for linear gradients (going from red to green shows a dark part in the middle). Same thing for everything actually, even if it not always obvious to detect. It would clearly be better if users had not to worry about it and get good results, optionally bypassing the system if they want more control.
Comment 5 Michael[tm] Smith 2011-08-04 05:03:52 UTC
mass-move component to LC1
Comment 6 Ian 'Hixie' Hickson 2011-09-21 17:54:34 UTC
EDITOR'S RESPONSE: This is an Editor's Response to your comment. If you are satisfied with this response, please change the state of this bug to CLOSED. If you have additional information and would like the editor to reconsider, please reopen this bug. If you would like to escalate the issue to the full HTML Working Group, please add the TrackerRequest keyword to this bug, and suggest title and text for the tracker issue; or you may create a tracker issue yourself, if you are able to do so. For more details, see this document:
   http://dev.w3.org/html5/decision-policy/decision-policy.html

Status: Rejected
Change Description: no spec change
Rationale: 

<canvas> has to use the same colour space as the rest of the Web, and that's already pretty much been resolved to be sRGB.

The spec does cover this:

http://www.whatwg.org/specs/web-apps/current-work/complete.html#color-spaces-and-color-correction