Skip to main content

cranpose_ui/modifier/
blur.rs

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