Skip to main content

ferroui_base/rendering/composition/drawing/
render_data_stream_replay.rs

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