aboutsummaryrefslogtreecommitdiff
path: root/src/render_helpers/shaders
diff options
context:
space:
mode:
Diffstat (limited to 'src/render_helpers/shaders')
-rw-r--r--src/render_helpers/shaders/border.frag174
-rw-r--r--src/render_helpers/shaders/mod.rs2
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),