This commit is contained in:
Kiko 2024-07-10 21:29:06 +00:00 committed by GitHub
commit 8e60b4f5ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 668 additions and 9 deletions

View File

@ -422,6 +422,8 @@ pub struct Gradient {
pub angle: i16,
#[knuffel(property, default)]
pub relative_to: GradientRelativeTo,
#[knuffel(property(name="in"), str, default)]
pub in_: GradientInterpolation,
}
#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]
@ -431,6 +433,30 @@ pub enum GradientRelativeTo {
WorkspaceView,
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct GradientInterpolation {
pub color_space: GradientColorSpace,
pub hue_interpol: HueInterpolation
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum GradientColorSpace {
#[default]
Srgb,
SrgbLinear,
Oklab,
Oklch,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum HueInterpolation {
#[default]
Shorter,
Longer,
Increasing,
Decreasing,
}
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct Border {
#[knuffel(child)]
@ -1423,6 +1449,55 @@ impl CornerRadius {
}
}
impl FromStr for GradientInterpolation {
type Err = miette::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = s.split_whitespace();
let in_part1 = iter.next();
let in_part2 = iter.next();
let in_part3 = iter.next();
let color = if in_part1 != None {
let in_str = in_part1.unwrap();
match in_str {
"srgb" => GradientColorSpace::Srgb,
"srgb-linear" => GradientColorSpace::SrgbLinear,
"oklab" => GradientColorSpace::Oklab,
"oklch" => GradientColorSpace::Oklch,
&_ => return Err(miette!("Invalid color-space: {in_str}"))
}
} else {
GradientColorSpace::Srgb
};
let interpolation = if in_part2 != None {
let in_str = in_part2.unwrap();
if color != GradientColorSpace::Oklch {
return Err(miette!("There's a value: {in_str} after a non polar colorspace"))
}
if in_part3 == None || in_part3.unwrap() != "hue" {
return Err(miette!("Invalid hue-interpolation: {in_str} you may be missing 'hue' at the end."))
} else if iter.next() == None {
match in_str {
"shorter" => HueInterpolation::Shorter,
"longer" => HueInterpolation::Longer,
"increasing" => HueInterpolation::Increasing,
"decreasing" => HueInterpolation::Decreasing,
&_ => return Err(miette!("Invalid hue-interpolation: {in_str}"))
}
} else {
// this is a placeholder and should be changed if anything is added to in
return Err(miette!("Theres a missing indicator hue from in "))
}
} else {
HueInterpolation::Shorter
};
Ok( Self { color_space: color, hue_interpol: interpolation } )
}
}
impl FromStr for Color {
type Err = miette::Error;

View File

@ -4,7 +4,7 @@ use std::time::Duration;
use niri::animation::ANIMATION_SLOWDOWN;
use niri::render_helpers::border::BorderRenderElement;
use niri_config::CornerRadius;
use niri_config::{CornerRadius, GradientInterpolation};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
@ -64,6 +64,7 @@ impl TestCase for GradientAngle {
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
GradientInterpolation::default(),
[1., 0., 0., 1.],
[0., 1., 0., 1.],
self.angle - FRAC_PI_2,

View File

@ -5,7 +5,7 @@ use std::time::Duration;
use niri::animation::ANIMATION_SLOWDOWN;
use niri::layout::focus_ring::FocusRing;
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{Color, CornerRadius, FloatOrInt};
use niri_config::{Color, CornerRadius, FloatOrInt, GradientInterpolation};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Point, Rectangle, Size};
@ -104,6 +104,7 @@ impl TestCase for GradientArea {
[BorderRenderElement::new(
area.size,
g_area,
GradientInterpolation::default(),
[1., 0., 0., 1.],
[0., 1., 0., 1.],
FRAC_PI_4,

View File

@ -0,0 +1,51 @@
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{CornerRadius, GradientInterpolation, HueInterpolation, GradientColorSpace};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
use super::TestCase;
pub struct GradientOklab {
gradient_format: GradientInterpolation,
}
impl GradientOklab {
pub fn new(_size: Size<i32, Logical>) -> Self {
Self {
gradient_format: GradientInterpolation{
color_space: GradientColorSpace::Oklab,
hue_interpol: HueInterpolation::Shorter
}
}
}
}
impl TestCase for GradientOklab {
fn render(
&mut self,
_renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
let (a, b) = (size.w / 6, size.h / 3);
let size = (size.w - a * 2, size.h - b * 2);
let area = Rectangle::from_loc_and_size((a, b), size).to_f64();
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
self.gradient_format,
[1., 0., 0., 1.],
[0., 1., 0., 1.],
0.,
Rectangle::from_loc_and_size((0., 0.), area.size),
0.,
CornerRadius::default(),
1.,
)
.with_location(area.loc)]
.into_iter()
.map(|elem| Box::new(elem) as _)
.collect()
}
}

View File

@ -0,0 +1,51 @@
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{CornerRadius, GradientInterpolation, HueInterpolation, GradientColorSpace};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
use super::TestCase;
pub struct GradientOklchDecreasing {
gradient_format: GradientInterpolation,
}
impl GradientOklchDecreasing {
pub fn new(_size: Size<i32, Logical>) -> Self {
Self {
gradient_format: GradientInterpolation{
color_space: GradientColorSpace::Oklch,
hue_interpol: HueInterpolation::Decreasing
}
}
}
}
impl TestCase for GradientOklchDecreasing {
fn render(
&mut self,
_renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
let (a, b) = (size.w / 6, size.h / 3);
let size = (size.w - a * 2, size.h - b * 2);
let area = Rectangle::from_loc_and_size((a, b), size).to_f64();
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
self.gradient_format,
[1., 0., 0., 1.],
[0., 1., 0., 1.],
0.,
Rectangle::from_loc_and_size((0., 0.), area.size),
0.,
CornerRadius::default(),
1.,
)
.with_location(area.loc)]
.into_iter()
.map(|elem| Box::new(elem) as _)
.collect()
}
}

View File

@ -0,0 +1,51 @@
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{CornerRadius, GradientInterpolation, HueInterpolation, GradientColorSpace};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
use super::TestCase;
pub struct GradientOklchIncreasing {
gradient_format: GradientInterpolation,
}
impl GradientOklchIncreasing {
pub fn new(_size: Size<i32, Logical>) -> Self {
Self {
gradient_format: GradientInterpolation{
color_space: GradientColorSpace::Oklch,
hue_interpol: HueInterpolation::Increasing
}
}
}
}
impl TestCase for GradientOklchIncreasing {
fn render(
&mut self,
_renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
let (a, b) = (size.w / 6, size.h / 3);
let size = (size.w - a * 2, size.h - b * 2);
let area = Rectangle::from_loc_and_size((a, b), size).to_f64();
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
self.gradient_format,
[1., 0., 0., 1.],
[0., 1., 0., 1.],
0.,
Rectangle::from_loc_and_size((0., 0.), area.size),
0.,
CornerRadius::default(),
1.,
)
.with_location(area.loc)]
.into_iter()
.map(|elem| Box::new(elem) as _)
.collect()
}
}

View File

@ -0,0 +1,51 @@
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{CornerRadius, GradientInterpolation, HueInterpolation, GradientColorSpace};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
use super::TestCase;
pub struct GradientOklchLonger {
gradient_format: GradientInterpolation,
}
impl GradientOklchLonger {
pub fn new(_size: Size<i32, Logical>) -> Self {
Self {
gradient_format: GradientInterpolation{
color_space: GradientColorSpace::Oklch,
hue_interpol: HueInterpolation::Longer
}
}
}
}
impl TestCase for GradientOklchLonger {
fn render(
&mut self,
_renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
let (a, b) = (size.w / 6, size.h / 3);
let size = (size.w - a * 2, size.h - b * 2);
let area = Rectangle::from_loc_and_size((a, b), size).to_f64();
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
self.gradient_format,
[1., 0., 0., 1.],
[0., 1., 0., 1.],
0.,
Rectangle::from_loc_and_size((0., 0.), area.size),
0.,
CornerRadius::default(),
1.,
)
.with_location(area.loc)]
.into_iter()
.map(|elem| Box::new(elem) as _)
.collect()
}
}

View File

@ -0,0 +1,51 @@
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{CornerRadius, GradientInterpolation, HueInterpolation, GradientColorSpace};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
use super::TestCase;
pub struct GradientOklchShorter {
gradient_format: GradientInterpolation,
}
impl GradientOklchShorter {
pub fn new(_size: Size<i32, Logical>) -> Self {
Self {
gradient_format: GradientInterpolation{
color_space: GradientColorSpace::Oklch,
hue_interpol: HueInterpolation::Shorter
}
}
}
}
impl TestCase for GradientOklchShorter {
fn render(
&mut self,
_renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
let (a, b) = (size.w / 6, size.h / 3);
let size = (size.w - a * 2, size.h - b * 2);
let area = Rectangle::from_loc_and_size((a, b), size).to_f64();
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
self.gradient_format,
[1., 0., 0., 1.],
[0., 1., 0., 1.],
0.,
Rectangle::from_loc_and_size((0., 0.), area.size),
0.,
CornerRadius::default(),
1.,
)
.with_location(area.loc)]
.into_iter()
.map(|elem| Box::new(elem) as _)
.collect()
}
}

View File

@ -0,0 +1,51 @@
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{CornerRadius, GradientInterpolation, HueInterpolation, GradientColorSpace};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
use super::TestCase;
pub struct GradientSrgb {
gradient_format: GradientInterpolation,
}
impl GradientSrgb {
pub fn new(_size: Size<i32, Logical>) -> Self {
Self {
gradient_format: GradientInterpolation{
color_space: GradientColorSpace::Srgb,
hue_interpol: HueInterpolation::Shorter
}
}
}
}
impl TestCase for GradientSrgb {
fn render(
&mut self,
_renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
let (a, b) = (size.w / 6, size.h / 3);
let size = (size.w - a * 2, size.h - b * 2);
let area = Rectangle::from_loc_and_size((a, b), size).to_f64();
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
self.gradient_format,
[1., 0., 0., 1.],
[0., 1., 0., 1.],
0.,
Rectangle::from_loc_and_size((0., 0.), area.size),
0.,
CornerRadius::default(),
1.,
)
.with_location(area.loc)]
.into_iter()
.map(|elem| Box::new(elem) as _)
.collect()
}
}

View File

@ -0,0 +1,51 @@
use niri::render_helpers::border::BorderRenderElement;
use niri_config::{CornerRadius, GradientInterpolation, HueInterpolation, GradientColorSpace};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Physical, Rectangle, Size};
use super::TestCase;
pub struct GradientSrgbLinear {
gradient_format: GradientInterpolation,
}
impl GradientSrgbLinear {
pub fn new(_size: Size<i32, Logical>) -> Self {
Self {
gradient_format: GradientInterpolation{
color_space: GradientColorSpace::SrgbLinear,
hue_interpol: HueInterpolation::Shorter
}
}
}
}
impl TestCase for GradientSrgbLinear {
fn render(
&mut self,
_renderer: &mut GlesRenderer,
size: Size<i32, Physical>,
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
let (a, b) = (size.w / 6, size.h / 3);
let size = (size.w - a * 2, size.h - b * 2);
let area = Rectangle::from_loc_and_size((a, b), size).to_f64();
[BorderRenderElement::new(
area.size,
Rectangle::from_loc_and_size((0., 0.), area.size),
self.gradient_format,
[1., 0., 0., 1.],
[0., 1., 0., 1.],
0.,
Rectangle::from_loc_and_size((0., 0.), area.size),
0.,
CornerRadius::default(),
1.,
)
.with_location(area.loc)]
.into_iter()
.map(|elem| Box::new(elem) as _)
.collect()
}
}

View File

@ -6,6 +6,13 @@ use smithay::utils::{Physical, Size};
pub mod gradient_angle;
pub mod gradient_area;
pub mod gradient_srgb;
pub mod gradient_srgblinear;
pub mod gradient_oklab;
pub mod gradient_oklch_shorter;
pub mod gradient_oklch_longer;
pub mod gradient_oklch_increasing;
pub mod gradient_oklch_decreasing;
pub mod layout;
pub mod tile;
pub mod window;

View File

@ -18,6 +18,13 @@ use tracing_subscriber::EnvFilter;
use crate::cases::gradient_angle::GradientAngle;
use crate::cases::gradient_area::GradientArea;
use crate::cases::gradient_srgb::GradientSrgb;
use crate::cases::gradient_srgblinear::GradientSrgbLinear;
use crate::cases::gradient_oklab::GradientOklab;
use crate::cases::gradient_oklch_shorter::GradientOklchShorter;
use crate::cases::gradient_oklch_longer::GradientOklchLonger;
use crate::cases::gradient_oklch_increasing::GradientOklchIncreasing;
use crate::cases::gradient_oklch_decreasing::GradientOklchDecreasing;
use crate::cases::layout::Layout;
use crate::cases::TestCase;
@ -112,6 +119,13 @@ fn build_ui(app: &adw::Application) {
s.add(GradientAngle::new, "Gradient - Angle");
s.add(GradientArea::new, "Gradient - Area");
s.add(GradientSrgb::new, "Gradient - Srgb");
s.add(GradientSrgbLinear::new, "Gradient - SrgbLinear");
s.add(GradientOklab::new, "Gradient - Oklab");
s.add(GradientOklchShorter::new, "Gradient - Oklch Shorter");
s.add(GradientOklchLonger::new, "Gradient - Oklch Longer");
s.add(GradientOklchIncreasing::new, "Gradient - Oklch Increasing");
s.add(GradientOklchDecreasing::new, "Gradient - Oklch Decreasing");
let content_headerbar = adw::HeaderBar::new();

View File

@ -153,6 +153,7 @@ layout {
// The angle is the same as in linear-gradient, and is optional,
// defaulting to 180 (top-to-bottom gradient).
// You can use any CSS linear-gradient tool on the web to set these up.
// Changing the color space is also supported, check the wiki for more info.
//
// active-gradient from="#80c8ff" to="#bbddff" angle=45

View File

@ -1,7 +1,7 @@
use std::iter::zip;
use arrayvec::ArrayVec;
use niri_config::{CornerRadius, Gradient, GradientRelativeTo};
use niri_config::{CornerRadius, Gradient, GradientRelativeTo, GradientInterpolation};
use smithay::backend::renderer::element::Kind;
use smithay::utils::{Logical, Point, Rectangle, Size};
@ -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,6 +179,7 @@ impl FocusRing {
border.update(
size,
Rectangle::from_loc_and_size(gradient_area.loc - loc, gradient_area.size),
gradient.in_,
gradient.from.into(),
gradient.to.into(),
((gradient.angle as f32) - 90.).to_radians(),
@ -198,6 +200,7 @@ impl FocusRing {
gradient_area.loc - self.locations[0],
gradient_area.size,
),
gradient.in_,
gradient.from.into(),
gradient.to.into(),
((gradient.angle as f32) - 90.).to_radians(),

View File

@ -1,7 +1,7 @@
use std::rc::Rc;
use std::time::Duration;
use niri_config::CornerRadius;
use niri_config::{CornerRadius, GradientInterpolation};
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::{Element, Kind};
use smithay::backend::renderer::gles::GlesRenderer;
@ -757,6 +757,7 @@ impl<W: LayoutElement> Tile<W> {
return BorderRenderElement::new(
geo.size,
Rectangle::from_loc_and_size((0., 0.), geo.size),
GradientInterpolation::default(),
elem.color(),
elem.color(),
0.,

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
use glam::{Mat3, Vec2};
use niri_config::CornerRadius;
use niri_config::{CornerRadius, GradientInterpolation, GradientColorSpace, 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,6 +28,7 @@ pub struct BorderRenderElement {
struct Parameters {
size: Size<f64, Logical>,
gradient_area: Rectangle<f64, Logical>,
gradient_format: GradientInterpolation,
color_from: [f32; 4],
color_to: [f32; 4],
angle: f32,
@ -43,6 +44,7 @@ impl BorderRenderElement {
pub fn new(
size: Size<f64, Logical>,
gradient_area: Rectangle<f64, Logical>,
gradient_format: GradientInterpolation,
color_from: [f32; 4],
color_to: [f32; 4],
angle: f32,
@ -57,6 +59,7 @@ impl BorderRenderElement {
params: Parameters {
size,
gradient_area,
gradient_format,
color_from,
color_to,
angle,
@ -77,6 +80,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,6 +101,7 @@ impl BorderRenderElement {
&mut self,
size: Size<f64, Logical>,
gradient_area: Rectangle<f64, Logical>,
gradient_format: GradientInterpolation,
color_from: [f32; 4],
color_to: [f32; 4],
angle: f32,
@ -108,6 +113,7 @@ impl BorderRenderElement {
let params = Parameters {
size,
gradient_area,
gradient_format,
color_from,
color_to,
angle,
@ -128,6 +134,7 @@ impl BorderRenderElement {
let Parameters {
size,
gradient_area,
gradient_format,
color_from,
color_to,
angle,
@ -162,11 +169,27 @@ 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_interpol {
HueInterpolation::Shorter => 0.,
HueInterpolation::Longer => 1.,
HueInterpolation::Increasing => 2.,
HueInterpolation::Decreasing => 3.,
};
self.inner.update(
size,
None,
scale,
vec![
Uniform::new("colorspace", colorspace),
Uniform::new("hue_interpolation", hue_interpolation),
Uniform::new("color_from", color_from),
Uniform::new("color_to", color_to),
Uniform::new("grad_offset", grad_offset.to_array()),

View File

@ -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,174 @@ uniform vec2 geo_size;
uniform vec4 outer_radius;
uniform float border_width;
float srgb_to_linear(float color) {
return pow(color, 2.2);
}
float linear_to_srgb(float color) {
return pow(color, 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 = vec3(
pow(lms.x, 1.0 / 3.0),
pow(lms.y, 1.0 / 3.0),
pow(lms.z, 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 = vec3(
pow(lms.x, 3.0),
pow(lms.y, 3.0),
pow(lms.z, 3.0)
);
return lms * lms_to_rgb;
}
vec4 color_mix(vec4 color1, vec4 color2, float color_ratio) {
// srgb
if (colorspace == 0.0) {
return mix(color1, color2, color_ratio);
}
vec4 color_out;
color1.rgb /= color1.a != 0.0 ? color1.a : 1.0;
color2.rgb /= color2.a != 0.0 ? color2.a : 1.0;
color1.rgb = vec3(
srgb_to_linear(color1.r),
srgb_to_linear(color1.g),
srgb_to_linear(color1.b)
);
color2.rgb = vec3(
srgb_to_linear(color2.r),
srgb_to_linear(color2.g),
srgb_to_linear(color2.b)
);
// srgb-linear
if (colorspace == 1.0) {
color_out = mix(
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 = mix(
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 = mix(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 vec4(
linear_to_srgb(color_out.r) * color_out.a,
linear_to_srgb(color_out.g) * color_out.a,
linear_to_srgb(color_out.b) * color_out.a,
color_out.a
);
}
vec4 gradient_color(vec2 coords) {
coords = coords + grad_offset;
@ -33,7 +203,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) {

View File

@ -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),

View File

@ -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::{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,6 +289,7 @@ impl Mapped {
return BorderRenderElement::new(
geo.size,
Rectangle::from_loc_and_size((0., 0.), geo.size),
GradientInterpolation::default(),
elem.color(),
elem.color(),
0.,

View File

@ -32,7 +32,7 @@ layout {
active-color "#ffc87f"
inactive-color "#505050"
// active-gradient from="#ffbb66" to="#ffc880" angle=45 relative-to="workspace-view"
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view" gradient-type="linear"
}
struts {
@ -169,7 +169,7 @@ layout {
inactive-color "#505050"
// active-gradient from="#ffbb66" to="#ffc880" angle=45 relative-to="workspace-view"
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view" gradient-type="linear"
}
}
```
@ -221,6 +221,9 @@ layout {
}
```
Gradients can be rendered with different kinds of color interpolation, this doesen't mean that the arguments the gradient takes are any different.
Except for an optional `in` argument which can take both a colorspace and a hue interpolation method if the color space is polar.
Gradients can be colored relative to windows individually (the default), or to the whole view of the workspace.
To do that, set `relative-to="workspace-view"`.
Here's a visual example: