Skip to main content

cranpose_ui/modifier/
blur.rs

1use std::rc::Rc;
2
3use cranpose_ui_graphics::{
4    BlurredEdgeTreatment, Density, Dp, GraphicsLayer, LayerShape, Px, RenderEffect,
5};
6
7use super::{Modifier, inspector_metadata};
8use crate::modifier_nodes::LazyGraphicsLayerElement;
9
10impl Modifier {
11    /// Apply a Gaussian blur effect to this composable's rendered content.
12    ///
13    /// `radius` is expressed in Dp and converted to px using the current render
14    /// density when modifier slices are evaluated.
15    ///
16    /// Compose parity: this defaults to bounded rectangular edge treatment.
17    pub fn blur(self, radius: Dp) -> Self {
18        self.blur_with_edge_treatment(radius, BlurredEdgeTreatment::default())
19    }
20
21    /// Apply an isotropic Gaussian blur with explicit edge treatment.
22    ///
23    /// Compose parity:
24    /// - bounded treatment clips to shape and uses clamp sampling
25    /// - unbounded treatment disables clip and uses decal sampling
26    pub fn blur_with_edge_treatment(
27        self,
28        radius: Dp,
29        edge_treatment: BlurredEdgeTreatment,
30    ) -> Self {
31        self.blur_xy(radius, radius, edge_treatment)
32    }
33
34    /// Apply a Gaussian blur effect with separate horizontal and vertical radii.
35    ///
36    /// Radii are expressed in Dp and converted to px at evaluation time.
37    pub fn blur_xy(self, radius_x: Dp, radius_y: Dp, edge_treatment: BlurredEdgeTreatment) -> Self {
38        if radius_x.0 <= 0.0 && radius_y.0 <= 0.0 && !edge_treatment.clip() {
39            return self;
40        }
41
42        let modifier = Self::with_element(LazyGraphicsLayerElement::new(Rc::new(move || {
43            let density = Density::from_scale(crate::render_state::current_density());
44            let radius_x_px = radius_x.to_px(density).max(Px::ZERO);
45            let radius_y_px = radius_y.to_px(density).max(Px::ZERO);
46            let render_effect = if radius_x_px.0 > 0.0 && radius_y_px.0 > 0.0 {
47                Some(RenderEffect::blur_xy(
48                    radius_x_px.0,
49                    radius_y_px.0,
50                    edge_treatment.tile_mode(),
51                ))
52            } else {
53                None
54            };
55            GraphicsLayer {
56                render_effect,
57                shape: edge_treatment.shape().unwrap_or(LayerShape::Rectangle),
58                clip: edge_treatment.clip(),
59                ..Default::default()
60            }
61        })))
62        .with_inspector_metadata(inspector_metadata("blur", move |info| {
63            info.add_property("radiusX", radius_x.0.to_string());
64            info.add_property("radiusY", radius_y.0.to_string());
65            info.add_property("edgeTreatment", format!("{edge_treatment:?}"));
66        }));
67        self.then(modifier)
68    }
69}