Skip to main content

ferroui_base/rendering/composition/drawing/
render_data_stream_replay.rs

1use super::{IRenderDataVisitor, RenderDataStream};
2use crate::media::{BoxShadows, IBrush, IPen, ImmediateDrawingContext, RenderOptions, TextOptions};
3use crate::platform::{IDrawingContextImpl, IGeometryImpl, IGlyphRunImpl};
4use crate::rendering::scene_graph::ICustomDrawOperation;
5use crate::{Matrix, Point, Rect, RoundedRect};
6use std::sync::Arc;
7
8#[derive(Clone, Copy, PartialEq, Eq)]
9enum ReplayScopeKind {
10    Clip,
11    GeometryClip,
12    Opacity,
13    OpacityMask,
14    Transform,
15    RenderOptions,
16    TextOptions,
17    Effect,
18}
19
20/// What a replayed push has to undo.
21#[derive(Clone, Copy)]
22pub struct ReplayScope {
23    kind: ReplayScopeKind,
24    active: bool,
25    saved_transform: Matrix,
26}
27
28impl ReplayScope {
29    fn new(kind: ReplayScopeKind, active: bool) -> Self {
30        Self { kind, active, saved_transform: Matrix::IDENTITY }
31    }
32}
33
34/// Replays recorded operations onto a platform drawing context.
35pub struct ReplayVisitor<'a> {
36    context: &'a mut dyn IDrawingContextImpl,
37}
38
39impl<'a> ReplayVisitor<'a> {
40    pub fn new(context: &'a mut dyn IDrawingContextImpl) -> Self {
41        Self { context }
42    }
43}
44
45impl IRenderDataVisitor for ReplayVisitor<'_> {
46    type Scope = ReplayScope;
47
48    fn stop_visiting(&self) -> bool {
49        false
50    }
51
52    fn on_draw_line(&mut self, server_pen: Option<&dyn IPen>, _client_pen: Option<&dyn IPen>, p1: Point, p2: Point) {
53        self.context.draw_line(server_pen, p1, p2);
54    }
55
56    fn on_draw_rectangle(
57        &mut self,
58        server_brush: Option<&dyn IBrush>,
59        server_pen: Option<&dyn IPen>,
60        _client_pen: Option<&dyn IPen>,
61        rect: RoundedRect,
62        box_shadows: &BoxShadows,
63    ) {
64        self.context.draw_rectangle(server_brush, server_pen, rect, box_shadows);
65    }
66
67    fn on_draw_ellipse(
68        &mut self,
69        server_brush: Option<&dyn IBrush>,
70        server_pen: Option<&dyn IPen>,
71        _client_pen: Option<&dyn IPen>,
72        rect: Rect,
73    ) {
74        self.context.draw_ellipse(server_brush, server_pen, rect);
75    }
76
77    fn on_draw_geometry(
78        &mut self,
79        server_brush: Option<&dyn IBrush>,
80        server_pen: Option<&dyn IPen>,
81        _client_pen: Option<&dyn IPen>,
82        geometry: Option<&Arc<dyn IGeometryImpl>>,
83    ) {
84        if let Some(geometry) = geometry {
85            self.context.draw_geometry(server_brush, server_pen, &**geometry);
86        }
87    }
88
89    fn on_draw_glyph_run(&mut self, server_brush: Option<&dyn IBrush>, glyph_run: Option<&std::sync::Arc<dyn IGlyphRunImpl>>) {
90        if let Some(glyph_run) = glyph_run {
91            self.context.draw_glyph_run(server_brush, &**glyph_run);
92        }
93    }
94
95    fn on_draw_bitmap(&mut self, bitmap: Option<&std::sync::Arc<crate::platform::SharedBitmapImpl>>, opacity: f64, source_rect: Rect, dest_rect: Rect) {
96        if let Some(bitmap) = bitmap {
97            self.context.draw_bitmap(&**bitmap, opacity, source_rect, dest_rect);
98        }
99    }
100
101    fn on_draw_custom(&mut self, operation: Option<&std::sync::Arc<dyn ICustomDrawOperation>>) {
102        if let Some(operation) = operation {
103            let mut context = ImmediateDrawingContext::borrowed(self.context);
104            operation.render(&mut context);
105            context.dispose();
106        }
107    }
108
109    fn on_push_clip(&mut self, clip: RoundedRect) -> ReplayScope {
110        self.context.push_clip_rounded(clip);
111        ReplayScope::new(ReplayScopeKind::Clip, true)
112    }
113
114    fn on_push_geometry_clip(&mut self, geometry: Option<&Arc<dyn IGeometryImpl>>) -> ReplayScope {
115        if let Some(geometry) = geometry {
116            self.context.push_geometry_clip(&**geometry);
117        }
118        ReplayScope::new(ReplayScopeKind::GeometryClip, geometry.is_some())
119    }
120
121    fn on_push_opacity(&mut self, opacity: f64) -> ReplayScope {
122        if opacity != 1.0 {
123            self.context.push_opacity(opacity, None);
124        }
125        ReplayScope::new(ReplayScopeKind::Opacity, opacity != 1.0)
126    }
127
128    fn on_push_opacity_mask(&mut self, brush: Option<&dyn IBrush>, bounds: Rect) -> ReplayScope {
129        if let Some(brush) = brush {
130            self.context.push_opacity_mask(brush, bounds);
131        }
132        ReplayScope::new(ReplayScopeKind::OpacityMask, brush.is_some())
133    }
134
135    fn on_push_transform(&mut self, matrix: Matrix) -> ReplayScope {
136        let saved = self.context.transform();
137        self.context.set_transform(matrix * saved);
138        ReplayScope { kind: ReplayScopeKind::Transform, active: true, saved_transform: saved }
139    }
140
141    fn on_push_render_options(&mut self, options: RenderOptions) -> ReplayScope {
142        self.context.push_render_options(options);
143        ReplayScope::new(ReplayScopeKind::RenderOptions, true)
144    }
145
146    fn on_push_text_options(&mut self, options: TextOptions) -> ReplayScope {
147        self.context.push_text_options(options);
148        ReplayScope::new(ReplayScopeKind::TextOptions, true)
149    }
150
151    fn on_push_effect(&mut self, effect: Option<&std::sync::Arc<dyn crate::media::IImmutableEffect>>, bounds: Rect) -> ReplayScope {
152        let mut active = false;
153        if let Some(effect) = effect {
154            if let Some(effect_impl) = self.context.as_drawing_context_impl_with_effects() {
155                effect_impl.push_effect(Some(bounds), &**effect);
156                active = true;
157            }
158        }
159        ReplayScope::new(ReplayScopeKind::Effect, active)
160    }
161
162    fn on_pop(&mut self, scope: ReplayScope) {
163        if !scope.active {
164            return;
165        }
166        match scope.kind {
167            ReplayScopeKind::Clip => self.context.pop_clip(),
168            ReplayScopeKind::GeometryClip => self.context.pop_geometry_clip(),
169            ReplayScopeKind::Opacity => self.context.pop_opacity(),
170            ReplayScopeKind::OpacityMask => self.context.pop_opacity_mask(),
171            ReplayScopeKind::Transform => self.context.set_transform(scope.saved_transform),
172            ReplayScopeKind::RenderOptions => self.context.pop_render_options(),
173            ReplayScopeKind::TextOptions => self.context.pop_text_options(),
174            ReplayScopeKind::Effect => {
175                if let Some(effect_impl) = self.context.as_drawing_context_impl_with_effects() {
176                    effect_impl.pop_effect();
177                }
178            }
179        }
180    }
181}
182
183impl RenderDataStream {
184    /// Replays the recorded operations onto a platform drawing context.
185    pub fn replay(&self, context: &mut dyn IDrawingContextImpl) {
186        let mut visitor = ReplayVisitor::new(context);
187        self.visit(&mut visitor);
188    }
189
190    /// Like [`replay`](Self::replay), with a reusable scope stack.
191    pub fn replay_with_scopes(&self, context: &mut dyn IDrawingContextImpl, scopes: &mut Vec<ReplayScope>) {
192        let mut visitor = ReplayVisitor::new(context);
193        self.visit_with_scopes(&mut visitor, scopes);
194    }
195}