diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-07-16 10:22:03 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-16 07:22:03 +0000 |
| commit | 3ace97660fde7fe1f0cc07a3925d1114af9a9c2f (patch) | |
| tree | 9d736a1d403875737566ad8817bb347a9e8056fe /src/render_helpers/shaders | |
| parent | 0824737757d10cbeb844871c3f67756ca969cf7c (diff) | |
| download | niri-3ace97660fde7fe1f0cc07a3925d1114af9a9c2f.tar.gz niri-3ace97660fde7fe1f0cc07a3925d1114af9a9c2f.tar.bz2 niri-3ace97660fde7fe1f0cc07a3925d1114af9a9c2f.zip | |
Implement gradient color interpolation option (#548)
* Added the better color averaging code (tested & functional)
* rustfmt
* Make Color f32 0..1, clarify premul/unpremul
* Fix imports and test name
* Premultiply gradient colors matching CSS
* Fix indentation
* fixup
* Add gradient image
---------
Co-authored-by: K's Thinkpad <K.T.Kraft@protonmail.com>
Diffstat (limited to 'src/render_helpers/shaders')
| -rw-r--r-- | src/render_helpers/shaders/border.frag | 174 | ||||
| -rw-r--r-- | src/render_helpers/shaders/mod.rs | 2 |
2 files changed, 175 insertions, 1 deletions
diff --git a/src/render_helpers/shaders/border.frag b/src/render_helpers/shaders/border.frag index fe121037..80030627 100644 --- a/src/render_helpers/shaders/border.frag +++ b/src/render_helpers/shaders/border.frag @@ -10,6 +10,8 @@ uniform float niri_scale; uniform vec2 niri_size; varying vec2 niri_v_coords; +uniform float colorspace; +uniform float hue_interpolation; uniform vec4 color_from; uniform vec4 color_to; uniform vec2 grad_offset; @@ -21,6 +23,176 @@ uniform vec2 geo_size; uniform vec4 outer_radius; uniform float border_width; +vec4 premul_rect(vec4 color) { + color.rgb *= color.a; + return color; +} + +vec4 premul_lch(vec4 color) { + color.xy *= color.a; + return color; +} + +vec4 unpremul_rect(vec4 color) { + if (color.a == 0.0) + return color; + + color.rgb /= color.a; + return color; +} + +vec4 unpremul_lch(vec4 color) { + if (color.a == 0.0) + return color; + + color.xy /= color.a; + return color; +} + +vec4 premul_mix_unpremul_rect(vec4 color1, vec4 color2, float ratio) { + vec4 mixed = mix(premul_rect(color1), premul_rect(color2), ratio); + return unpremul_rect(mixed); +} + +vec4 premul_mix_unpremul_lch(vec4 color1, vec4 color2, float ratio) { + vec4 mixed = mix(premul_lch(color1), premul_lch(color2), ratio); + return unpremul_lch(mixed); +} + +vec3 srgb_to_linear(vec3 color) { + return pow(color, vec3(2.2)); +} + +vec3 linear_to_srgb(vec3 color) { + return pow(color, vec3(1.0 / 2.2)); +} + +vec3 lab_to_lch(vec3 color) { + float c = sqrt(pow(color.y, 2.0) + pow(color.z, 2.0)); + float h = degrees(atan(color.z, color.y)) ; + h += h <= 0.0 ? + 360.0 : + 0.0 ; + return vec3( + color.x, + c, + h + ); +} + +vec3 lch_to_lab(vec3 color) { + float a = color.y * clamp(cos(radians(color.z)), -1.0, 1.0); + float b = color.y * clamp(sin(radians(color.z)), -1.0, 1.0); + return vec3( + color.x, + a, + b + ); +} + +vec3 linear_to_oklab(vec3 color){ + mat3 rgb_to_lms = mat3( + vec3(0.4122214708, 0.5363325363, 0.0514459929), + vec3(0.2119034982, 0.6806995451, 0.1073969566), + vec3(0.0883024619, 0.2817188376, 0.6299787005) + ); + mat3 lms_to_oklab = mat3( + vec3(0.2104542553, 0.7936177850, -0.0040720468), + vec3(1.9779984951, -2.4285922050, 0.4505937099), + vec3(0.0259040371, 0.7827717662, -0.8086757660) + ); + vec3 lms = color * rgb_to_lms; + lms = pow(lms, vec3(1.0 / 3.0)); + return lms * lms_to_oklab; +} + +vec3 oklab_to_linear(vec3 color){ + mat3 oklab_to_lms = mat3( + vec3(1.0, 0.3963377774, 0.2158037573), + vec3(1.0, -0.1055613458, -0.0638541728), + vec3(1.0, -0.0894841775, -1.2914855480) + ); + mat3 lms_to_rgb = mat3( + vec3(4.0767416621, -3.3077115913, 0.2309699292), + vec3(-1.2684380046, 2.6097574011, -0.3413193965), + vec3(-0.0041960863, -0.7034186147, 1.7076147010) + ); + vec3 lms = color * oklab_to_lms; + lms = pow(lms, vec3(3.0)); + return lms * lms_to_rgb; +} + +vec4 color_mix(vec4 color1, vec4 color2, float color_ratio) { + vec4 color_out; + + // srgb + if (colorspace == 0.0) { + return mix(premul_rect(color1), premul_rect(color2), color_ratio); + } + + color1.rgb = srgb_to_linear(color1.rgb); + color2.rgb = srgb_to_linear(color2.rgb); + + // srgb-linear + if (colorspace == 1.0) { + color_out = premul_mix_unpremul_rect(color1, color2, color_ratio); + // oklab + } else if (colorspace == 2.0) { + color1.xyz = linear_to_oklab(color1.rgb); + color2.xyz = linear_to_oklab(color2.rgb); + color_out = premul_mix_unpremul_rect(color1, color2, color_ratio); + color_out.rgb = oklab_to_linear(color_out.xyz); + // oklch + } else if (colorspace == 3.0) { + color1.xyz = lab_to_lch(linear_to_oklab(color1.rgb)); + color2.xyz = lab_to_lch(linear_to_oklab(color2.rgb)); + color_out = premul_mix_unpremul_lch(color1, color2, color_ratio); + + float min_hue = min(color1.z, color2.z); + float max_hue = max(color1.z, color2.z); + float path_direct_distance = (max_hue - min_hue) * color_ratio; + float path_mod_distance = (360.0 - max_hue + min_hue) * color_ratio; + + float path_mod = + color1.z == min_hue ? + mod(color1.z - path_mod_distance, 360.0) : + mod(color1.z + path_mod_distance, 360.0) ; + float path_direct = + color1.z == min_hue ? + color1.z + path_direct_distance : + color1.z - path_direct_distance ; + + // shorter + if (hue_interpolation == 0.0) { + color_out.z = + max_hue - min_hue > 360.0 - max_hue + min_hue ? + path_mod : + path_direct ; + // longer + } else if (hue_interpolation == 1.0) { + color_out.z = + max_hue - min_hue <= 360.0 - max_hue + min_hue ? + path_mod : + path_direct ; + // increasing + } else if (hue_interpolation == 2.0) { + color_out.z = + color1.z > color2.z ? + path_mod : + path_direct ; + // decreasing + } else if (hue_interpolation == 3.0) { + color_out.z = + color1.z <= color2.z ? + path_mod : + path_direct ; + } + color_out.rgb = clamp(oklab_to_linear(lch_to_lab(color_out.xyz)), 0.0, 1.0); + } + + return premul_rect(vec4(linear_to_srgb(color_out.rgb), color_out.a)); +} + vec4 gradient_color(vec2 coords) { coords = coords + grad_offset; @@ -33,7 +205,7 @@ vec4 gradient_color(vec2 coords) { frac += 1.0; frac = clamp(frac, 0.0, 1.0); - return mix(color_from, color_to, frac); + return color_mix(color_from, color_to, frac); } float rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) { diff --git a/src/render_helpers/shaders/mod.rs b/src/render_helpers/shaders/mod.rs index b4824931..91ba32d1 100644 --- a/src/render_helpers/shaders/mod.rs +++ b/src/render_helpers/shaders/mod.rs @@ -34,6 +34,8 @@ impl Shaders { renderer, include_str!("border.frag"), &[ + UniformName::new("colorspace", UniformType::_1f), + UniformName::new("hue_interpolation", UniformType::_1f), UniformName::new("color_from", UniformType::_4f), UniformName::new("color_to", UniformType::_4f), UniformName::new("grad_offset", UniformType::_2f), |
