aboutsummaryrefslogtreecommitdiff
path: root/src/render_helpers
diff options
context:
space:
mode:
Diffstat (limited to 'src/render_helpers')
-rw-r--r--src/render_helpers/border.rs43
-rw-r--r--src/render_helpers/shaders/border.frag174
-rw-r--r--src/render_helpers/shaders/mod.rs2
3 files changed, 209 insertions, 10 deletions
diff --git a/src/render_helpers/border.rs b/src/render_helpers/border.rs
index c3442425..c0ab6663 100644
--- a/src/render_helpers/border.rs
+++ b/src/render_helpers/border.rs
@@ -1,7 +1,9 @@
use std::collections::HashMap;
use glam::{Mat3, Vec2};
-use niri_config::CornerRadius;
+use niri_config::{
+ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,
+};
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform};
use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};
@@ -28,8 +30,9 @@ pub struct BorderRenderElement {
struct Parameters {
size: Size<f64, Logical>,
gradient_area: Rectangle<f64, Logical>,
- color_from: [f32; 4],
- color_to: [f32; 4],
+ gradient_format: GradientInterpolation,
+ color_from: Color,
+ color_to: Color,
angle: f32,
geometry: Rectangle<f64, Logical>,
border_width: f32,
@@ -43,8 +46,9 @@ impl BorderRenderElement {
pub fn new(
size: Size<f64, Logical>,
gradient_area: Rectangle<f64, Logical>,
- color_from: [f32; 4],
- color_to: [f32; 4],
+ gradient_format: GradientInterpolation,
+ color_from: Color,
+ color_to: Color,
angle: f32,
geometry: Rectangle<f64, Logical>,
border_width: f32,
@@ -57,6 +61,7 @@ impl BorderRenderElement {
params: Parameters {
size,
gradient_area,
+ gradient_format,
color_from,
color_to,
angle,
@@ -77,6 +82,7 @@ impl BorderRenderElement {
params: Parameters {
size: Default::default(),
gradient_area: Default::default(),
+ gradient_format: GradientInterpolation::default(),
color_from: Default::default(),
color_to: Default::default(),
angle: 0.,
@@ -97,8 +103,9 @@ impl BorderRenderElement {
&mut self,
size: Size<f64, Logical>,
gradient_area: Rectangle<f64, Logical>,
- color_from: [f32; 4],
- color_to: [f32; 4],
+ gradient_format: GradientInterpolation,
+ color_from: Color,
+ color_to: Color,
angle: f32,
geometry: Rectangle<f64, Logical>,
border_width: f32,
@@ -108,6 +115,7 @@ impl BorderRenderElement {
let params = Parameters {
size,
gradient_area,
+ gradient_format,
color_from,
color_to,
angle,
@@ -128,6 +136,7 @@ impl BorderRenderElement {
let Parameters {
size,
gradient_area,
+ gradient_format,
color_from,
color_to,
angle,
@@ -162,13 +171,29 @@ impl BorderRenderElement {
let input_to_geo =
Mat3::from_scale(area_size) * Mat3::from_translation(-geo_loc / area_size);
+ let colorspace = match gradient_format.color_space {
+ GradientColorSpace::Srgb => 0.,
+ GradientColorSpace::SrgbLinear => 1.,
+ GradientColorSpace::Oklab => 2.,
+ GradientColorSpace::Oklch => 3.,
+ };
+
+ let hue_interpolation = match gradient_format.hue_interpolation {
+ HueInterpolation::Shorter => 0.,
+ HueInterpolation::Longer => 1.,
+ HueInterpolation::Increasing => 2.,
+ HueInterpolation::Decreasing => 3.,
+ };
+
self.inner.update(
size,
None,
scale,
vec![
- Uniform::new("color_from", color_from),
- Uniform::new("color_to", color_to),
+ Uniform::new("colorspace", colorspace),
+ Uniform::new("hue_interpolation", hue_interpolation),
+ Uniform::new("color_from", color_from.to_array_unpremul()),
+ Uniform::new("color_to", color_to.to_array_unpremul()),
Uniform::new("grad_offset", grad_offset.to_array()),
Uniform::new("grad_width", w),
Uniform::new("grad_vec", grad_vec.to_array()),
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),