From 48f0f6fb3ceb68fdb559ab38c8dcbb7b9ba3a13e Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Wed, 21 Feb 2024 21:27:44 +0400 Subject: Implement gradient borders --- src/render_helpers/gradient.rs | 143 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/render_helpers/gradient.rs (limited to 'src/render_helpers/gradient.rs') diff --git a/src/render_helpers/gradient.rs b/src/render_helpers/gradient.rs new file mode 100644 index 00000000..1dbed05a --- /dev/null +++ b/src/render_helpers/gradient.rs @@ -0,0 +1,143 @@ +use std::f32::consts::{self, FRAC_PI_2, PI}; + +use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; +use smithay::backend::renderer::gles::element::PixelShaderElement; +use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform}; +use smithay::backend::renderer::utils::CommitCounter; +use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform}; + +use super::primary_gpu_pixel_shader::PrimaryGpuPixelShaderRenderElement; +use super::renderer::NiriRenderer; +use super::shaders::Shaders; +use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; + +/// Renders a sub- or super-rect of an angled linear gradient like CSS linear-gradient(angle, a, b). +#[derive(Debug)] +pub struct GradientRenderElement(PrimaryGpuPixelShaderRenderElement); + +impl GradientRenderElement { + pub fn new( + renderer: &mut impl NiriRenderer, + scale: Scale, + area: Rectangle, + gradient_area: Rectangle, + color_from: [f32; 4], + color_to: [f32; 4], + mut angle: f32, + ) -> Option { + let shader = Shaders::get(renderer).gradient_border.clone()?; + let g_offset = (area.loc - gradient_area.loc).to_f64().to_physical(scale); + + let g_size = gradient_area.size.to_f64().to_physical(scale); + let (w, h) = (g_size.w as f32, g_size.h as f32); + let g_area_angle = f32::atan2(h, w); + let g_area_diag = f32::hypot(h, w); + + // Normalize the angle to [0°; 360°). + while angle < 0. { + angle += consts::TAU; + } + while angle >= consts::TAU { + angle -= consts::TAU; + } + + let angle_diag_to_grad = + if (0. ..=FRAC_PI_2).contains(&angle) || (PI..=PI + FRAC_PI_2).contains(&angle) { + angle - g_area_angle + } else { + (PI - angle) - g_area_angle + }; + let g_total = angle_diag_to_grad.cos().abs() * g_area_diag; + + let elem = PixelShaderElement::new( + shader, + area, + None, + 1., + vec![ + Uniform::new("color_from", color_from), + Uniform::new("color_to", color_to), + Uniform::new("angle", angle), + Uniform::new("gradient_offset", (g_offset.x as f32, g_offset.y as f32)), + Uniform::new("gradient_width", w), + Uniform::new("gradient_total", g_total), + ], + Kind::Unspecified, + ); + Some(Self(PrimaryGpuPixelShaderRenderElement(elem))) + } +} + +impl Element for GradientRenderElement { + fn id(&self) -> &Id { + self.0.id() + } + + fn current_commit(&self) -> CommitCounter { + self.0.current_commit() + } + + fn geometry(&self, scale: Scale) -> Rectangle { + self.0.geometry(scale) + } + + fn transform(&self) -> Transform { + self.0.transform() + } + + fn src(&self) -> Rectangle { + self.0.src() + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> Vec> { + self.0.damage_since(scale, commit) + } + + fn opaque_regions(&self, scale: Scale) -> Vec> { + self.0.opaque_regions(scale) + } + + fn alpha(&self) -> f32 { + self.0.alpha() + } + + fn kind(&self) -> Kind { + self.0.kind() + } +} + +impl RenderElement for GradientRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + ) -> Result<(), GlesError> { + RenderElement::::draw(&self.0, frame, src, dst, damage) + } + + fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option { + self.0.underlying_storage(renderer) + } +} + +impl<'render> RenderElement> for GradientRenderElement { + fn draw( + &self, + frame: &mut TtyFrame<'_, '_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + ) -> Result<(), TtyRendererError<'render>> { + RenderElement::>::draw(&self.0, frame, src, dst, damage) + } + + fn underlying_storage(&self, renderer: &mut TtyRenderer<'render>) -> Option { + self.0.underlying_storage(renderer) + } +} -- cgit