Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-color-4] extend rgb() for HDR #3249

Closed
profezzorn opened this issue Oct 25, 2018 · 11 comments
Closed

[css-color-4] extend rgb() for HDR #3249

profezzorn opened this issue Oct 25, 2018 · 11 comments

Comments

@profezzorn
Copy link

We should extend rgb() and rgba() to accept values outside of 0-255, and values outside of 0-100%. This would allow you to specify colors outside of the sRGB gamut using extended sRGB.

In order to get sufficient precision for some colors, we should also allow floating-point values, with the assumption that the decimals may be ignored on some platforms.

It's worth noting that it's not easy to extend hsl() in the same way, since the definition of hsl() automatically keeps all values within the RGB cube.

Here is a description of how extended sRGB works:

Extended sRGB
So what does it mean to specify rgb(510, 510, -100) ? While 510 is twice as much as 255, it doesn’t actually mean “twice as much red” or “twice as much blue”. To understand we first look at the sRGB transfer function:

sRGB2Linear(x) = x <= 0.04045 ? x / 12.92 : ((x + 0.055) / 1.055) ^ 2.4  {  0 <= x <= 1 }

Normally, sRGB only uses x values between 0 and 1, but it’s easy to see that the function can handle any value greater than zero. Negative values is a little trickier, but we can get around that by mirroring the srgb function into the negative range. This gives us the extended sRGB (xsRGB) function:

xsRGB2Linear(x) = x < 0 : -sRGB2Linear(-x) : sRGB2Linear(x)  { -∞ <= x <= ∞  }

Now we can easily compute that xsRGB2Linear(511/255) = 4.97, or nearly 5x brighter than the value 255.

For -100, the RGB the transfer function gives us:
xsRGB2Linear(-100/255) = -xsRGB2Linear(100/255) = -0.1274

@nigelmegitt
Copy link

What does a negative value mean here? You aren't intending to imply the existence of a display that can suck light in as well as emitting it?

@svgeesus
Copy link
Contributor

We should extend rgb() and rgba() to accept values outside of 0-255, and values outside of 0-100%. This would allow you to specify colors outside of the sRGB gamut using extended sRGB.

You would think this would at least give a wider color gamut (WCG) (and indeed, I used to argue so) but in fact, no, it doesn't for several reasons. Firstly, the transfer curve is undefined outside the 0% to 100% range. But okay, this can be dealt with in various ways (and you suggest several).

Secondly, having done so, there are still colors inside the Lab surface gamut - or even, inside the Rec.2020 gamut - which are unrepresentable in an extended but gamma-encoded sRGB colorspace. Basically the curve flattens off too much.

OK so thirdly, one could use a linearized, extended sRGB space. But then, the advantage of sticking with sRGB for familiarity becomes even less and the precision required (16bit, or half float) is greater.

Lastly, existing color management systems still tend to either clip to 0-100%, or are poorly tested beyond those limits.

The correct way to encode a wider color gamut is to use a wider gamut colorspace (like P3, or 2020, or Lab) and existing color management systems will handle that just fine. That is the approach taken in CSS Color 4.

The correct way to handle high dynamic range (HDR) is to use a transfer function designed to cope with the much larger dynamic range, such as PQ or HLG, plus enough bits per component to encode it (and usually, a WCG colorspace as well).

@svgeesus
Copy link
Contributor

What does a negative value mean here? You aren't intending to imply the existence of a display that can suck light in as well as emitting it?

Nothing of the sort. But once a colorspace is linearized (and thus, is additive), and assuming sufficient precision is available, it can be shown that any 3-component colorspace can be transformed into another one by a simple matrix multiplication. If the destination colorspace has a smaller gamut, then some values may be greater than 100% or less than 0%.
As an example, the original RGB colorspace used for color matching experiments in the 1930s required negative red values to match some spectral colors. This is (one of the reasons) why the CIE XYZ colorspace was develped, so all possible spctral colors could be represented with positive values of the X, Y and Z primaries. (As a consequence, the X Y and Z primaries are supersaturated and not physically realizable, which does not matter as long as the math works out).
And more recently, it was found that even XYZ sometimes needs slightly negative values (if colors measured under one lighting are chromatically adapted to a different lighting condition). And again, existing systems often failed to handle those negative values.

So to summarize the issue - extending below 0% and above 100% seems like it might work, and can work in a theoretical context with linearized (non gamma corrected) colorspaces and high precision; but is not a good way forward and is better handled in other ways. Primarily, by using a wider color gamut (like the P3 gamut used on many Apple, Samsung, Dell and HP devices and by digital cinema projectors; or the Rec.2020 system used by broadcast television and movie studios).

@profezzorn
Copy link
Author

Saying that this is "not a good way forward" seems highly subjective, and a bit odd since this is already how Chrome, Microsoft Windows, Apple and many games actually implement HDR. Any system that deals with inputs in many color spaces is going to be using some representation that can contain all of them internally, and using extended sRGB with half-floats, either in linear or non-linear form is exactly that. Having implemented both color management and HDR in chrome, I find it much easier to work with extended sRGB than lots of different color spaces. Extended sRGB is also entirely backwards compatible, which makes it easy to mix legacy and HDR content.

Pointing out that additional precision is needed seems redundant when I already suggested that floating-point values should be allowed. Additional precision is required to implement HDR, regardless of what color space is chosen, so that point seems moot.

How would you guys suggest that a color like rgb(688.17, 688.17, 688.17) (which is 10x brighter than rgb(255,255,255) in my proposed scheme) should be written?

@ccameron-chromium
Copy link
Contributor

This proposal -- extending the sRGB color space to cover the range -infty to infty in a way that is well-defined and as-un-surprising-as-possible -- is consistent with what canvas is aiming to do to support both Wide Color Gamut (WCG) and and High Dynamic Range (HDR). I'm not sure if the canvas docs have been updated to this effect, but that is the plan (which had wide buy-in last time it was discussed in person).

Note that this is also consistent with CoreGraphics' kCGColorSpaceExtendedSRGB
https://developer.apple.com/documentation/coregraphics/kcgcolorspaceextendedsrgb?language=objc

@mounirlamouri
Copy link
Member

@svgeesus can this issue be re-considered given @ccameron-chromium comment above and usage in various platforms (inc. Windows and Apple APIs) as @profezzorn pointed out?

@svgeesus
Copy link
Contributor

svgeesus commented Nov 3, 2018

Sure, I am re-opening it. I'm aware that canvas will use a half-float linear sRGB space and certainly, CSS could use that too; but for clarity and backwards compatibility, not mixed up with the existing rgb() or hex syntax which indicates a gamma-corrected space.

@ccameron-chromium
Copy link
Contributor

I'm aware that canvas will use a half-float linear sRGB

IIUC canvas will not use linear sRGB space. Rather, it will use the extended-sRGB transfer function. This is much better for adoption and compatibility, and has less surprise.

With a linear-transfer-function space for canvas, adding WCG or HDR support requires a full application. With extended-sRGB-transfer-function space, WCG and HDR can be added with no side-effects.

@svgeesus
Copy link
Contributor

The proposals for extending Canvas to a half-float, extended-sRGB wide color gamut are:

Note that these currently add WCG and extended bit depth (half float) but do not add HDR support.

@svgeesus
Copy link
Contributor

svgeesus commented Jun 8, 2020

These canvas proposals seem to be stagnating, with un-answered review comments.

@svgeesus svgeesus added css-color-hdr CSS HDR extension and removed css-color-4 Current Work labels Sep 5, 2020
@svgeesus
Copy link
Contributor

Updating this ol issue to account for recent changes:

  • CSS color 4 now defines all the predefined RGB spaces over an unbounded interval, not just 0.0 to 1.0. Transfer functions are mirrored around the origin
  • an srgb-linear predefined colorspace has been added, for convenience of other specifications which use it (Canvas, SVG filters) but also exposed as CSS syntax and now implemented in Safari

Closing (again) as accepted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants