Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/gfx_apis/vulkan/shaders/eotfs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ vec3 inv_eotf_st240(vec3 c) {
}

vec3 eotf_log100(vec3 c) {
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))
);
Comment on lines -70 to +74
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.

}

vec3 inv_eotf_log100(vec3 c) {
Expand All @@ -80,7 +84,11 @@ vec3 inv_eotf_log100(vec3 c) {
}

vec3 eotf_log316(vec3 c) {
return pow(vec3(10), vec3(2.5) * (c - vec3(1.0)));
return mix(
vec3(0.0),
pow(vec3(10), vec3(2.5) * (c - vec3(1.0))),
greaterThan(c, vec3(0.0))
);
}

vec3 inv_eotf_log316(vec3 c) {
Expand Down
Binary file modified src/gfx_apis/vulkan/shaders_bin/out.frag.spv
Binary file not shown.
Binary file modified src/gfx_apis/vulkan/shaders_bin/tex.frag.spv
Binary file not shown.
2 changes: 1 addition & 1 deletion src/gfx_apis/vulkan/shaders_hash.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
302a9f250bdc4f8e0e71a9f77c9a8a7aa55fd003bc91c2422a700c4abd83f54e src/gfx_apis/vulkan/shaders/alpha_modes.glsl
b6a0df1e231fab533499329636b7a580384784418baee06c147af5fcc384cf5c src/gfx_apis/vulkan/shaders/eotfs.glsl
87979e5a9cde1db1af97549294879362fbbcab72ad89aba64b5576a26b597d66 src/gfx_apis/vulkan/shaders/eotfs.glsl
8a38df18851cd13884499820f26939fb7319f45d913d867f254d8118d59fb117 src/gfx_apis/vulkan/shaders/fill.common.glsl
21c488d12aa5ad2f109ec44cb856dfe837e02ea9025b5ed64439d742c17cbf30 src/gfx_apis/vulkan/shaders/fill.frag
4fb481d8d73afdfb0d8f077eb8665d86f06c8a32a91e44ed369ef5dff554646d src/gfx_apis/vulkan/shaders/fill.vert
Expand Down
12 changes: 10 additions & 2 deletions src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,18 @@ impl Color {
}
}
fn log100(c: f32) -> f32 {
10.0.powf(2.0 * (c - 1.0))
if c <= 0.0 {
0.0
} else {
10.0.powf(2.0 * (c - 1.0))
}
}
fn log316(c: f32) -> f32 {
10.0.powf(2.5 * (c - 1.0))
if c <= 0.0 {
0.0
} else {
10.0.powf(2.5 * (c - 1.0))
}
}
fn st428(c: f32) -> f32 {
c.powf(2.6) * 52.37 / 48.0
Expand Down
Loading