aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/layout/focus_ring.rs15
-rw-r--r--src/layout/tile.rs7
-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
-rw-r--r--src/window/mapped.rs7
6 files changed, 226 insertions, 22 deletions
diff --git a/src/layout/focus_ring.rs b/src/layout/focus_ring.rs
index 97a1b75a..7cd73760 100644
--- a/src/layout/focus_ring.rs
+++ b/src/layout/focus_ring.rs
@@ -1,7 +1,7 @@
use std::iter::zip;
use arrayvec::ArrayVec;
-use niri_config::{CornerRadius, Gradient, GradientRelativeTo};
+use niri_config::{CornerRadius, Gradient, GradientInterpolation, GradientRelativeTo};
use smithay::backend::renderer::element::Kind;
use smithay::utils::{Logical, Point, Rectangle, Size};
@@ -72,7 +72,7 @@ impl FocusRing {
};
for buf in &mut self.buffers {
- buf.set_color(color.into());
+ buf.set_color(color.to_array_premul());
}
let radius = radius.fit_to(self.full_size.w as f32, self.full_size.h as f32);
@@ -91,6 +91,7 @@ impl FocusRing {
to: color,
angle: 0,
relative_to: GradientRelativeTo::Window,
+ in_: GradientInterpolation::default(),
});
let full_rect = Rectangle::from_loc_and_size((-width, -width), self.full_size);
@@ -178,8 +179,9 @@ impl FocusRing {
border.update(
size,
Rectangle::from_loc_and_size(gradient_area.loc - loc, gradient_area.size),
- gradient.from.into(),
- gradient.to.into(),
+ gradient.in_,
+ gradient.from,
+ gradient.to,
((gradient.angle as f32) - 90.).to_radians(),
Rectangle::from_loc_and_size(full_rect.loc - loc, full_rect.size),
rounded_corner_border_width,
@@ -198,8 +200,9 @@ impl FocusRing {
gradient_area.loc - self.locations[0],
gradient_area.size,
),
- gradient.from.into(),
- gradient.to.into(),
+ gradient.in_,
+ gradient.from,
+ gradient.to,
((gradient.angle as f32) - 90.).to_radians(),
Rectangle::from_loc_and_size(full_rect.loc - self.locations[0], full_rect.size),
rounded_corner_border_width,
diff --git a/src/layout/tile.rs b/src/layout/tile.rs
index 710add83..167ff2c6 100644
--- a/src/layout/tile.rs
+++ b/src/layout/tile.rs
@@ -1,7 +1,7 @@
use std::rc::Rc;
use std::time::Duration;
-use niri_config::CornerRadius;
+use niri_config::{Color, CornerRadius, GradientInterpolation};
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::{Element, Kind};
use smithay::backend::renderer::gles::GlesRenderer;
@@ -757,8 +757,9 @@ impl<W: LayoutElement> Tile<W> {
return BorderRenderElement::new(
geo.size,
Rectangle::from_loc_and_size((0., 0.), geo.size),
- elem.color(),
- elem.color(),
+ GradientInterpolation::default(),
+ Color::from_array_premul(elem.color()),
+ Color::from_array_premul(elem.color()),
0.,
Rectangle::from_loc_and_size((0., 0.), geo.size),
0.,
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),
diff --git a/src/window/mapped.rs b/src/window/mapped.rs
index 178b0a30..9963d7b6 100644
--- a/src/window/mapped.rs
+++ b/src/window/mapped.rs
@@ -2,7 +2,7 @@ use std::cell::{Cell, RefCell};
use std::cmp::{max, min};
use std::time::Duration;
-use niri_config::{CornerRadius, WindowRule};
+use niri_config::{Color, CornerRadius, GradientInterpolation, WindowRule};
use smithay::backend::renderer::element::surface::render_elements_from_surface_tree;
use smithay::backend::renderer::element::{Id, Kind};
use smithay::backend::renderer::gles::GlesRenderer;
@@ -289,8 +289,9 @@ impl Mapped {
return BorderRenderElement::new(
geo.size,
Rectangle::from_loc_and_size((0., 0.), geo.size),
- elem.color(),
- elem.color(),
+ GradientInterpolation::default(),
+ Color::from_array_premul(elem.color()),
+ Color::from_array_premul(elem.color()),
0.,
Rectangle::from_loc_and_size((0., 0.), geo.size),
0.,