Skip to main content

cranpose_ui/modifier/
blur.rs

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