standards-positions icon indicating copy to clipboard operation
standards-positions copied to clipboard

CanvasCompositing globalHDRHeadroom attribute

Open ccameron-chromium opened this issue 4 months ago • 7 comments

WebKittens

No response

Title of the proposal

CanvasCompositing globalHDRHeadroom attribute

URL to the spec

https://github.com/ccameron-chromium/ColorWeb-CG/blob/master/canvas2d_hdr_headroom.md

URL to the spec's repository

No response

Issue Tracker URL

https://github.com/whatwg/html/issues/11165

Explainer URL

No response

TAG Design Review URL

https://github.com/w3ctag/design-reviews/issues/917

Mozilla standards-positions issue URL

No response

WebKit Bugzilla URL

No response

Radar URL

No response

Description

This allows specifying the HDR headroom for drawing content to 2D canvas. There exist analogous APIs for WebGL and WebGPU when copying content to a 2D texture. See:

  • https://github.com/gpuweb/gpuweb/issues/5236
  • https://github.com/KhronosGroup/WebGL/issues/3735

ccameron-chromium avatar Aug 26 '25 15:08 ccameron-chromium

Other CanvasCompositing properties globalAlpha and globalCompositeOperation have similar CSS properties. Should not CSS have a property for the headroom? For example this CSS

div { headroom: 4.5; }

will tone map all the drawing on the <div> element to this headroom. This will make sure all the pixels on this element are tone mapped to the same headroom regardless they came from images or from color-hdr.

shallawa avatar Aug 26 '25 19:08 shallawa

After the div is tone mapped to headroom:4.5, what happens if the display only has headroom 2. Does it just clip then?

(There was some thought about allowing absolute headrooms to dynamic-range-limit, but so far we haven't seen a compelling need, and I think WebKit was arguing against it)

ccameron-chromium avatar Aug 26 '25 20:08 ccameron-chromium

@ccameron-chromium I discussed with WebKit but it is unfortunate we are suggesting to use log2 encoding as opposed to linear encoding like CGContext uses with https://developer.apple.com/documentation/coregraphics/cgcontext/setedrtargetheadroom(_:)=objc for instance

The linear convention used by CG allows a 0 default to mean unconstrained whereas with log2 requires passing Infinity for the same behavior. It seems odd that an API surface would require passing Infinity for a common use case.

We would prefer linear encoding and a default of 0 with a similar meaning to the CG API header comment for CGContextSetEDRTargetHeadroom in CGContext.h:

Please note that the headroom value of 0.0f means "headroom unknown" which prevents tone mapping.

mwyrzykowski avatar Oct 01 '25 20:10 mwyrzykowski

Re log vs linear, we'll have to do this consistently across all APIs, and we can defer deciding that. I have issues the value 0 being used in this way, but we can defer discussing that.

Let's focus on this issue, since it keeps coming up:

We would prefer [...] a default of 0 0 default to mean unconstrained

I want to make sure I understand this.

You are proposing that all images be rendered at their maximum HDR-ness, by default (as you were proposing over here as well).

The consequence of this would be that all HDR content rendered into a 2D context that has default initialization would be clipped to SDR.

In particular, consider this page: https://ccameron-chromium.github.io/ColorWeb-CG/canvas2d_hdr_headroom_example.html It does a trivial "load image and call drawImage on a canvas" for two images. The image on the left is a PQ PNG image and the image on the right is an ISO 21496-1 JPEG image.

Today, this is how it renders in Safari and all other browsers (with the exception of the image on the left in Firefox): Image

The behavior you are proposing is as follows: Image

Notice that all of the highlights are blown out.

Am I misunderstanding this?

ccameron-chromium avatar Oct 02 '25 13:10 ccameron-chromium

I assume that CGContextSetEDRTargetHeadroom is for setting the content headroom?

Functionally, whether using linear values or log2 values, 0.0f for a content headroom never results in tonemapping (unless the display hypothetically had a headroom below 1.0 linear or 0.0f log2, but that would likely break all sorts of assumptions), so you always get clipping if it exceeds the display headroom - aside from documenting this fact I don't see any contention over the behavior of 0.0f in either linear or log2?

As for the linear vs log2 matter itself, I am strongly in favor of linear, with my main rationale being that there's no precedent for log2 values in any web specs (even WebGL/WebGPU have log2 only in context of mipmapping and that serves a different purpose and goes the opposite direction), and it is somewhat intuitive when specifying linear colors in CSS style syntax that you need to use RGB values less than the specified headroom that goes with them.

Edit: One thing to clarify - I am assuming a tonemapping function like Reinhard (equation 4 specifically) where the content headroom (the term Lw) being <= output headroom (which was assumed to be 1.0 in Reinhard but is easily made configurable) intuitively results in a no-op transfer function, and even if the content tonemapping function is active it's still possible for content pixels to exceed the specified content headroom and not directly break anything by doing so.

ladyhavoc avatar Nov 13 '25 09:11 ladyhavoc

As for the linear vs log2 matter itself, I am strongly in favor of linear, with my main rationale being that there's no precedent for log2 values in any web specs

I'm pretty much the lone holdout thinking that log2 is maybe a good idea. So unless there's a huge push for log2 from somewhere, we'll probably go with linear.

I'll want to make sure to ventilate the issue in a forum that includes everyone from 2D/WebGL/GPU and CSS (ColorWeb-CG?) before giving up the issue completely.

the content headroom (the term Lw) being <= output headroom results in a no-op [tone mapping] function

Yes, that's correct. If "content headroom" <= "output headroom", then the tone mapping is a no-op[*]. Pixel values can be greater than the "content headroom" value, and they shouldn't get clamped to the "content headroom" (but they will get clamped to the "output headroom")

[*] Except if there exists an alternate representation at a higher headroom

ccameron-chromium avatar Nov 13 '25 11:11 ccameron-chromium

I've moved the "linear versus log2" discussion to https://github.com/w3c/ColorWeb-CG/issues/129, which needs to decide it for CSS/2D/WebGL/WebGPU. The direction appears to be "linear is preferred", but I want to make sure all APIs cover this. (And I'm copy-pasting this message into a handful of different bugs on this issue, sorry for the spam if you see it twice).

ccameron-chromium avatar Nov 21 '25 15:11 ccameron-chromium