diff options
27 files changed, 1092 insertions, 79 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index e5998bfb..6396f6dc 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -410,8 +410,8 @@ impl Default for FocusRing { Self { off: false, width: FloatOrInt(4.), - active_color: Color::new(127, 200, 255, 255), - inactive_color: Color::new(80, 80, 80, 255), + active_color: Color::from_rgba8_unpremul(127, 200, 255, 255), + inactive_color: Color::from_rgba8_unpremul(80, 80, 80, 255), active_gradient: None, inactive_gradient: None, } @@ -428,6 +428,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)] @@ -437,6 +439,30 @@ pub enum GradientRelativeTo { WorkspaceView, } +#[derive(Default, Debug, Clone, Copy, PartialEq)] +pub struct GradientInterpolation { + pub color_space: GradientColorSpace, + pub hue_interpolation: 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)] @@ -458,8 +484,8 @@ impl Default for Border { Self { off: true, width: FloatOrInt(4.), - active_color: Color::new(255, 200, 127, 255), - inactive_color: Color::new(80, 80, 80, 255), + active_color: Color::from_rgba8_unpremul(255, 200, 127, 255), + inactive_color: Color::from_rgba8_unpremul(80, 80, 80, 255), active_gradient: None, inactive_gradient: None, } @@ -492,23 +518,49 @@ impl From<FocusRing> for Border { } } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +/// RGB color in [0, 1] with unpremultiplied alpha. +#[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Color { - pub r: u8, - pub g: u8, - pub b: u8, - pub a: u8, + pub r: f32, + pub g: f32, + pub b: f32, + pub a: f32, } impl Color { - pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self { + pub const fn new_unpremul(r: f32, g: f32, b: f32, a: f32) -> Self { Self { r, g, b, a } } -} -impl From<Color> for [f32; 4] { - fn from(c: Color) -> Self { - let [r, g, b, a] = [c.r, c.g, c.b, c.a].map(|x| x as f32 / 255.); + pub fn from_rgba8_unpremul(r: u8, g: u8, b: u8, a: u8) -> Self { + Self::from_array_unpremul([r, g, b, a].map(|x| x as f32 / 255.)) + } + + pub fn from_array_premul([r, g, b, a]: [f32; 4]) -> Self { + let a = a.clamp(0., 1.); + + if a == 0. { + Self::new_unpremul(0., 0., 0., 0.) + } else { + Self { + r: (r / a).clamp(0., 1.), + g: (g / a).clamp(0., 1.), + b: (b / a).clamp(0., 1.), + a, + } + } + } + + pub fn from_array_unpremul([r, g, b, a]: [f32; 4]) -> Self { + Self { r, g, b, a } + } + + pub fn to_array_unpremul(self) -> [f32; 4] { + [self.r, self.g, self.b, self.a] + } + + pub fn to_array_premul(self) -> [f32; 4] { + let [r, g, b, a] = [self.r, self.g, self.b, self.a]; [r * a, g * a, b * a, a] } } @@ -1429,12 +1481,73 @@ 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 Some(in_part1) = in_part1 else { + return Err(miette!("missing color space")); + }; + + let color = match in_part1 { + "srgb" => GradientColorSpace::Srgb, + "srgb-linear" => GradientColorSpace::SrgbLinear, + "oklab" => GradientColorSpace::Oklab, + "oklch" => GradientColorSpace::Oklch, + x => { + return Err(miette!( + "invalid color space {x}; can be srgb, srgb-linear, oklab or oklch" + )) + } + }; + + let interpolation = if let Some(in_part2) = in_part2 { + if color != GradientColorSpace::Oklch { + return Err(miette!("only oklch color space can have hue interpolation")); + } + + if in_part3 != Some("hue") { + return Err(miette!( + "interpolation must end with \"hue\", like \"oklch shorter hue\"" + )); + } else if iter.next().is_some() { + return Err(miette!("unexpected text after hue interpolation")); + } else { + match in_part2 { + "shorter" => HueInterpolation::Shorter, + "longer" => HueInterpolation::Longer, + "increasing" => HueInterpolation::Increasing, + "decreasing" => HueInterpolation::Decreasing, + x => { + return Err(miette!( + "invalid hue interpolation {x}; \ + can be shorter, longer, increasing, decreasing" + )) + } + } + } + } else { + HueInterpolation::default() + }; + + Ok(Self { + color_space: color, + hue_interpolation: interpolation, + }) + } +} + impl FromStr for Color { type Err = miette::Error; fn from_str(s: &str) -> Result<Self, Self::Err> { - let [r, g, b, a] = csscolorparser::parse(s).into_diagnostic()?.to_rgba8(); - Ok(Self { r, g, b, a }) + let color = csscolorparser::parse(s).into_diagnostic()?.to_array(); + Ok(Self::from_array_unpremul(color.map(|x| x as f32))) } } @@ -1453,7 +1566,7 @@ struct ColorRgba { impl From<ColorRgba> for Color { fn from(value: ColorRgba) -> Self { let ColorRgba { r, g, b, a } = value; - Self { r, g, b, a } + Self::from_array_unpremul([r, g, b, a].map(|x| x as f32 / 255.)) } } @@ -2771,41 +2884,25 @@ mod tests { focus_ring: FocusRing { off: false, width: FloatOrInt(5.), - active_color: Color { - r: 0, - g: 100, - b: 200, - a: 255, - }, - inactive_color: Color { - r: 255, - g: 200, - b: 100, - a: 0, - }, + active_color: Color::from_rgba8_unpremul(0, 100, 200, 255), + inactive_color: Color::from_rgba8_unpremul(255, 200, 100, 0), active_gradient: Some(Gradient { - from: Color::new(10, 20, 30, 255), - to: Color::new(0, 128, 255, 255), + from: Color::from_rgba8_unpremul(10, 20, 30, 255), + to: Color::from_rgba8_unpremul(0, 128, 255, 255), angle: 180, relative_to: GradientRelativeTo::WorkspaceView, + in_: GradientInterpolation { + color_space: GradientColorSpace::Srgb, + hue_interpolation: HueInterpolation::Shorter, + }, }), inactive_gradient: None, }, border: Border { off: false, width: FloatOrInt(3.), - active_color: Color { - r: 255, - g: 200, - b: 127, - a: 255, - }, - inactive_color: Color { - r: 255, - g: 200, - b: 100, - a: 0, - }, + active_color: Color::from_rgba8_unpremul(255, 200, 127, 255), + inactive_color: Color::from_rgba8_unpremul(255, 200, 100, 0), active_gradient: None, inactive_gradient: None, }, @@ -3096,6 +3193,81 @@ mod tests { } #[test] + fn parse_gradient_interpolation() { + assert_eq!( + "srgb".parse::<GradientInterpolation>().unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::Srgb, + ..Default::default() + } + ); + assert_eq!( + "srgb-linear".parse::<GradientInterpolation>().unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::SrgbLinear, + ..Default::default() + } + ); + assert_eq!( + "oklab".parse::<GradientInterpolation>().unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::Oklab, + ..Default::default() + } + ); + assert_eq!( + "oklch".parse::<GradientInterpolation>().unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::Oklch, + ..Default::default() + } + ); + assert_eq!( + "oklch shorter hue" + .parse::<GradientInterpolation>() + .unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::Oklch, + hue_interpolation: HueInterpolation::Shorter, + } + ); + assert_eq!( + "oklch longer hue".parse::<GradientInterpolation>().unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::Oklch, + hue_interpolation: HueInterpolation::Longer, + } + ); + assert_eq!( + "oklch decreasing hue" + .parse::<GradientInterpolation>() + .unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::Oklch, + hue_interpolation: HueInterpolation::Decreasing, + } + ); + assert_eq!( + "oklch increasing hue" + .parse::<GradientInterpolation>() + .unwrap(), + GradientInterpolation { + color_space: GradientColorSpace::Oklch, + hue_interpolation: HueInterpolation::Increasing, + } + ); + + assert!("".parse::<GradientInterpolation>().is_err()); + assert!("srgb shorter hue".parse::<GradientInterpolation>().is_err()); + assert!("oklch shorter".parse::<GradientInterpolation>().is_err()); + assert!("oklch shorter h".parse::<GradientInterpolation>().is_err()); + assert!("oklch a hue".parse::<GradientInterpolation>().is_err()); + assert!("oklch shorter hue a" + .parse::<GradientInterpolation>() + .is_err()); + } + + #[test] fn parse_iso_level3_shift() { assert_eq!( "ISO_Level3_Shift+A".parse::<Key>().unwrap(), diff --git a/niri-visual-tests/src/cases/gradient_angle.rs b/niri-visual-tests/src/cases/gradient_angle.rs index 7d0b7542..fa70dc73 100644 --- a/niri-visual-tests/src/cases/gradient_angle.rs +++ b/niri-visual-tests/src/cases/gradient_angle.rs @@ -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::{Color, CornerRadius, GradientInterpolation}; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Logical, Physical, Rectangle, Size}; @@ -64,8 +64,9 @@ impl TestCase for GradientAngle { [BorderRenderElement::new( area.size, Rectangle::from_loc_and_size((0., 0.), area.size), - [1., 0., 0., 1.], - [0., 1., 0., 1.], + GradientInterpolation::default(), + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(0., 1., 0., 1.), self.angle - FRAC_PI_2, Rectangle::from_loc_and_size((0., 0.), area.size), 0., diff --git a/niri-visual-tests/src/cases/gradient_area.rs b/niri-visual-tests/src/cases/gradient_area.rs index 13f50d4c..5463cfc4 100644 --- a/niri-visual-tests/src/cases/gradient_area.rs +++ b/niri-visual-tests/src/cases/gradient_area.rs @@ -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}; @@ -23,7 +23,7 @@ impl GradientArea { let border = FocusRing::new(niri_config::FocusRing { off: false, width: FloatOrInt(1.), - active_color: Color::new(255, 255, 255, 128), + active_color: Color::from_rgba8_unpremul(255, 255, 255, 128), inactive_color: Color::default(), active_gradient: None, inactive_gradient: None, @@ -104,8 +104,9 @@ impl TestCase for GradientArea { [BorderRenderElement::new( area.size, g_area, - [1., 0., 0., 1.], - [0., 1., 0., 1.], + GradientInterpolation::default(), + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(0., 1., 0., 1.), FRAC_PI_4, Rectangle::from_loc_and_size((0, 0), rect_size).to_f64(), 0., diff --git a/niri-visual-tests/src/cases/gradient_oklab.rs b/niri-visual-tests/src/cases/gradient_oklab.rs new file mode 100644 index 00000000..abebb213 --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_oklab.rs @@ -0,0 +1,53 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, +}; +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_interpolation: 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, + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(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() + } +} diff --git a/niri-visual-tests/src/cases/gradient_oklab_alpha.rs b/niri-visual-tests/src/cases/gradient_oklab_alpha.rs new file mode 100644 index 00000000..31ae59ef --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_oklab_alpha.rs @@ -0,0 +1,51 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation}; +use smithay::backend::renderer::element::RenderElement; +use smithay::backend::renderer::gles::GlesRenderer; +use smithay::utils::{Logical, Physical, Rectangle, Size}; + +use super::TestCase; + +pub struct GradientOklabAlpha { + gradient_format: GradientInterpolation, +} + +impl GradientOklabAlpha { + pub fn new(_size: Size<i32, Logical>) -> Self { + Self { + gradient_format: GradientInterpolation { + color_space: GradientColorSpace::Oklab, + hue_interpolation: Default::default(), + }, + } + } +} + +impl TestCase for GradientOklabAlpha { + 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, + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(0., 1., 0., 0.), + 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() + } +} diff --git a/niri-visual-tests/src/cases/gradient_oklch_alpha.rs b/niri-visual-tests/src/cases/gradient_oklch_alpha.rs new file mode 100644 index 00000000..022f59ec --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_oklch_alpha.rs @@ -0,0 +1,53 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, +}; +use smithay::backend::renderer::element::RenderElement; +use smithay::backend::renderer::gles::GlesRenderer; +use smithay::utils::{Logical, Physical, Rectangle, Size}; + +use super::TestCase; + +pub struct GradientOklchAlpha { + gradient_format: GradientInterpolation, +} + +impl GradientOklchAlpha { + pub fn new(_size: Size<i32, Logical>) -> Self { + Self { + gradient_format: GradientInterpolation { + color_space: GradientColorSpace::Oklch, + hue_interpolation: HueInterpolation::Longer, + }, + } + } +} + +impl TestCase for GradientOklchAlpha { + 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, + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(0., 1., 0., 0.), + 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() + } +} diff --git a/niri-visual-tests/src/cases/gradient_oklch_decreasing.rs b/niri-visual-tests/src/cases/gradient_oklch_decreasing.rs new file mode 100644 index 00000000..7039f2c8 --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_oklch_decreasing.rs @@ -0,0 +1,53 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, +}; +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_interpolation: 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, + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(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() + } +} diff --git a/niri-visual-tests/src/cases/gradient_oklch_increasing.rs b/niri-visual-tests/src/cases/gradient_oklch_increasing.rs new file mode 100644 index 00000000..2a020923 --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_oklch_increasing.rs @@ -0,0 +1,53 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, +}; +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_interpolation: 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, + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(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() + } +} diff --git a/niri-visual-tests/src/cases/gradient_oklch_longer.rs b/niri-visual-tests/src/cases/gradient_oklch_longer.rs new file mode 100644 index 00000000..d63259fd --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_oklch_longer.rs @@ -0,0 +1,53 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, +}; +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_interpolation: 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, + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(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() + } +} diff --git a/niri-visual-tests/src/cases/gradient_oklch_shorter.rs b/niri-visual-tests/src/cases/gradient_oklch_shorter.rs new file mode 100644 index 00000000..7cd412ab --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_oklch_shorter.rs @@ -0,0 +1,53 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, +}; +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_interpolation: 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, + Color::new_unpremul(1., 0., 0., 1.), + Color::new_unpremul(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() + } +} diff --git a/niri-visual-tests/src/cases/gradient_srgb.rs b/niri-visual-tests/src/cases/gradient_srgb.rs new file mode 100644 index 00000000..d0b847c0 --- /dev/null +++ b/niri-visual-tests/src/cases/gradient_srgb.rs @@ -0,0 +1,53 @@ +use niri::render_helpers::border::BorderRenderElement; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, +}; +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, |
