Skip to content

cmm: Make log_100 and log_316 TFs symmetric#826

Open
kennylevinsen wants to merge 1 commit intomahkoh:masterfrom
kennylevinsen:log_eotf
Open

cmm: Make log_100 and log_316 TFs symmetric#826
kennylevinsen wants to merge 1 commit intomahkoh:masterfrom
kennylevinsen:log_eotf

Conversation

@kennylevinsen
Copy link
Copy Markdown

log_100 and log_316 has the treshold implemented in the inverse EOTF, but missing from the EOTF. This causes black from a client buffer to be elevated to 0.01 and 0.00316 respectively, which can be seen in wl-colortest as having the black brightness tile become grey as you switch through the TFs.

Add the threshold to the EOTF so that 0 means black.

log_100 and log_316 has the treshold implemented in the inverse EOTF,
but missing from the EOTF. This causes black from a client buffer to be
elevated to 0.01 and 0.00316 respectively, which can be seen in
wl-colortest as having the black brightness tile become grey as you
switch through the TFs.

Add the threshold to the EOTF so that 0 means black.
Comment on lines -70 to +74
return pow(vec3(10), vec3(2.0) * (c - vec3(1.0)));
return mix(
vec3(0.0),
pow(vec3(10), vec3(2.0) * (c - vec3(1.0))),
greaterThan(c, vec3(0.0))
);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should just clamp(c, 0.0, 1.0) like the other SDR transfer functions.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that when c is zero, pow(vec3(10), vec3(2.0) * (c - vec3(1.0))) yields 0.01, so a clamp wouldn't help.

The approach is similar to compound_power_2_4. and matches what is done in their respective inverse EOTF's (a mix over greaterThanEqual(c, vec3(0.01)) and greaterThanEqual(c, vec3(sqrt(10)/1000)), respectively).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I see with your current code is that it creates a discontinuity at 0. I.e. E=0 maps to O=0 but E=epsilon maps to O=0.01. For a single black tile that would be fine, but I'm not sure if it would be fine for a gradient or any image that just so happens to have a black pixel.

Copy link
Copy Markdown
Author

@kennylevinsen kennylevinsen Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The step in log_316 is small enough that it is unlikely to matter, but log100 clearly shows the discontinuity in the gradient (images below).

Searching around a bit I don't see a whole lot of implementations to reference, but the fix matches gstreamer and the color-science python package. moxcms does neither and instead uses the midpoint to minimize the maximum error. Not a whole lot of support out there for these TFs though, so don't really have much in way of a benchmark or convention...

Plain gamma22:
image

Before this fix:
image

After this fix (note the left side of the gradient bar):
image

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to use 0 as the fixed point, which is what all other EOTFs do right now, then maybe applying an affine function could work:

vec3 eotf_log100(vec3 c) {
    c = clamp(c, 0.0, 1.0);
    c = pow(vec3(10), vec3(2.0) * (c - vec3(1.0)));
    return (c - vec3(0.01)) / vec3(1.0 - 0.01);
}

vec3 inv_eotf_log100(vec3 c) {
    c = clamp(c, 0.0, 1.0);
    c = c * vec3(1.0 - 0.01) + vec3(0.01);
    return vec3(1.0) + log2(c) / vec3(log2(10)) / vec3(2.0);
}

However, I'd prefer to standardize these things in https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/staging/color-management/appendix.md.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants