Skip to main content

ferroui_base/rendering/composition/drawing/
render_data_stream_bounds.rs

1use super::{IRenderDataVisitor, RenderDataStream};
2use crate::media::{BoxShadows, IBrush, IEffect, IPen, RenderOptions, TextOptions};
3use crate::platform::{IGeometryImpl, IGlyphRunImpl};
4use crate::rendering::scene_graph::{ICustomDrawOperation, LineBoundsHelper};
5use crate::{Matrix, Point, Rect, RoundedRect, Thickness};
6use std::sync::Arc;
7
8/// The bounds accumulated outside of a scope, and how to map the bounds of
9/// the scope's content when it ends.
10#[derive(Clone, Copy)]
11pub struct BoundsScope {
12    saved_bounds: Option<Rect>,
13    is_transform: bool,
14    matrix: Matrix,
15    effect_padding: Thickness,
16}
17
18/// Computes the bounds of recorded operations.
19#[derive(Default)]
20pub struct BoundsVisitor {
21    pub current: Option<Rect>,
22}
23
24impl BoundsVisitor {
25    fn enter_child_scope(&mut self, is_transform: bool, matrix: Matrix, effect_padding: Thickness) -> BoundsScope {
26        let scope = BoundsScope { saved_bounds: self.current, is_transform, matrix, effect_padding };
27        self.current = None;
28        scope
29    }
30
31    fn plain_scope(&mut self) -> BoundsScope {
32        self.enter_child_scope(false, Matrix::default(), Thickness::default())
33    }
34
35    fn union(&mut self, rect: Option<Rect>) {
36        self.current = Rect::union_optional(self.current, rect);
37    }
38}
39
40impl IRenderDataVisitor for BoundsVisitor {
41    type Scope = BoundsScope;
42
43    fn stop_visiting(&self) -> bool {
44        false
45    }
46
47    fn on_draw_line(&mut self, server_pen: Option<&dyn IPen>, _client_pen: Option<&dyn IPen>, p1: Point, p2: Point) {
48        if let Some(server_pen) = server_pen {
49            self.union(Some(LineBoundsHelper::calculate_bounds(p1, p2, server_pen)));
50        }
51    }
52
53    fn on_draw_rectangle(
54        &mut self,
55        _server_brush: Option<&dyn IBrush>,
56        server_pen: Option<&dyn IPen>,
57        _client_pen: Option<&dyn IPen>,
58        rect: RoundedRect,
59        box_shadows: &BoxShadows,
60    ) {
61        let bounds = box_shadows.transform_bounds(rect.rect).inflate(server_pen.map_or(0.0, |p| p.thickness()) / 2.0);
62        self.union(Some(bounds));
63    }
64
65    fn on_draw_ellipse(
66        &mut self,
67        _server_brush: Option<&dyn IBrush>,
68        server_pen: Option<&dyn IPen>,
69        _client_pen: Option<&dyn IPen>,
70        rect: Rect,
71    ) {
72        self.union(Some(rect.inflate(server_pen.map_or(0.0, |p| p.thickness()))));
73    }
74
75    fn on_draw_geometry(
76        &mut self,
77        _server_brush: Option<&dyn IBrush>,
78        server_pen: Option<&dyn IPen>,
79        _client_pen: Option<&dyn IPen>,
80        geometry: Option<&Arc<dyn IGeometryImpl>>,
81    ) {
82        self.union(Some(geometry.map(|g| g.get_render_bounds(server_pen)).unwrap_or_default()));
83    }
84
85    fn on_draw_glyph_run(&mut self, _server_brush: Option<&dyn IBrush>, glyph_run: Option<&std::sync::Arc<dyn IGlyphRunImpl>>) {
86        self.union(Some(glyph_run.map(|g| g.bounds()).unwrap_or_default()));
87    }
88
89    fn on_draw_bitmap(&mut self, _bitmap: Option<&std::sync::Arc<crate::platform::SharedBitmapImpl>>, _opacity: f64, _source_rect: Rect, dest_rect: Rect) {
90        self.union(Some(dest_rect));
91    }
92
93    fn on_draw_custom(&mut self, operation: Option<&std::sync::Arc<dyn ICustomDrawOperation>>) {
94        self.union(operation.map(|o| o.bounds()));
95    }
96
97    fn on_push_clip(&mut self, _clip: RoundedRect) -> BoundsScope {
98        self.plain_scope()
99    }
100
101    fn on_push_geometry_clip(&mut self, _geometry: Option<&Arc<dyn IGeometryImpl>>) -> BoundsScope {
102        self.plain_scope()
103    }
104
105    fn on_push_opacity(&mut self, _opacity: f64) -> BoundsScope {
106        self.plain_scope()
107    }
108
109    fn on_push_opacity_mask(&mut self, _brush: Option<&dyn IBrush>, _bounds: Rect) -> BoundsScope {
110        self.plain_scope()
111    }
112
113    fn on_push_transform(&mut self, matrix: Matrix) -> BoundsScope {
114        self.enter_child_scope(true, matrix, Thickness::default())
115    }
116
117    fn on_push_render_options(&mut self, _options: RenderOptions) -> BoundsScope {
118        self.plain_scope()
119    }
120
121    fn on_push_text_options(&mut self, _options: TextOptions) -> BoundsScope {
122        self.plain_scope()
123    }
124
125    fn on_push_effect(&mut self, effect: Option<&std::sync::Arc<dyn crate::media::IImmutableEffect>>, _bounds: Rect) -> BoundsScope {
126        let padding = crate::media::EffectExtensions::get_effect_output_padding(effect.map(|e| &**e as &dyn IEffect));
127        self.enter_child_scope(false, Matrix::default(), padding)
128    }
129
130    fn on_pop(&mut self, scope: BoundsScope) {
131        let mut child_union = self.current;
132        if scope.is_transform {
133            child_union = child_union.map(|r| r.transform_to_aabb(scope.matrix));
134        } else if scope.effect_padding != Thickness::default() {
135            child_union = child_union.map(|r| r.inflate_thickness(scope.effect_padding));
136        }
137        self.current = Rect::union_optional(scope.saved_bounds, child_union);
138    }
139}
140
141impl RenderDataStream {
142    /// The bounds of everything the stream draws, or `None` if it draws
143    /// nothing.
144    pub fn calculate_bounds(&self) -> Option<Rect> {
145        let mut visitor = BoundsVisitor::default();
146        self.visit(&mut visitor);
147        visitor.current
148    }
149}