ferroui_base/rendering/composition/drawing/
render_data_stream_bounds.rs1use 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#[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#[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 pub fn calculate_bounds(&self) -> Option<Rect> {
145 let mut visitor = BoundsVisitor::default();
146 self.visit(&mut visitor);
147 visitor.current
148 }
149}