Skip to main content

ferroui_base/rendering/composition/drawing/
render_data_drawing_context.rs

1use super::server_resource_helper_extensions::{
2    brush_get_server, brush_render_resource, geometry_get_server, geometry_render_resource, is_immutable_brush,
3    is_immutable_pen, pen_get_client, pen_get_server, pen_render_resource,
4};
5use super::{CompositionRenderData, ICompositionRenderResource, ImmediateRenderDataSceneBrushContent, RenderDataStream};
6use crate::media::{
7    BoxShadows, EffectExtensions, Geometry, GlyphRun, IBrush, IDrawingContextCore, IEffect, IPen, ITileBrush,
8    RenderOptions, TextOptions,
9};
10use crate::platform::IGeometryImpl;
11use crate::rendering::composition::Compositor;
12use crate::rendering::scene_graph::ICustomDrawOperation;
13use crate::{Matrix, Point, Rect, Ref, RoundedRect};
14use std::collections::HashSet;
15use std::rc::Rc;
16use std::sync::Arc;
17
18#[derive(Clone, Copy, Default)]
19struct PushEntry {
20    emitted: bool,
21    position_before: usize,
22    position_after: usize,
23    depth_before: i32,
24}
25
26/// A resource whose reference on the compositor is held by the render data
27/// being recorded.
28pub(crate) enum RecordedResource {
29    Brush(Rc<dyn IBrush>),
30    Pen(Rc<dyn IPen>),
31    Geometry(Ref<Geometry>),
32}
33
34impl RecordedResource {
35    pub(crate) fn with_resource<R>(&self, f: impl FnOnce(&dyn ICompositionRenderResource) -> R) -> Option<R> {
36        match self {
37            RecordedResource::Brush(brush) => brush_render_resource(&**brush).map(f),
38            RecordedResource::Pen(pen) => pen_render_resource(&**pen).map(f),
39            RecordedResource::Geometry(geometry) => geometry_render_resource(geometry).map(f),
40        }
41    }
42}
43
44/// The drawing context core that records what is drawn into render data
45/// instead of drawing it.
46///
47/// With a compositor the result is a [`CompositionRenderData`] registered
48/// for serialization to the server; without one the recorded stream refers
49/// to the drawn objects directly and is replayed on the same thread.
50pub struct RenderDataDrawingContext {
51    compositor: Option<Rc<Compositor>>,
52    stream: Option<RenderDataStream>,
53    resources: Vec<RecordedResource>,
54    resources_hash_set: HashSet<usize>,
55    push_stack: Vec<PushEntry>,
56}
57
58impl RenderDataDrawingContext {
59    pub fn new(compositor: Option<Rc<Compositor>>) -> Self {
60        Self { compositor, stream: None, resources: Vec::new(), resources_hash_set: HashSet::new(), push_stack: Vec::new() }
61    }
62
63    fn stream(&mut self) -> &mut RenderDataStream {
64        self.stream.get_or_insert_with(RenderDataStream::new)
65    }
66
67    fn add_brush_resource(&mut self, brush: Option<&Rc<dyn IBrush>>) {
68        let (Some(compositor), Some(brush)) = (&self.compositor, brush) else { return };
69        // Immutable brushes and composition brushes are referenced directly.
70        if is_immutable_brush(&**brush) || brush.as_composition_brush().is_some() {
71            return;
72        }
73        let Some(resource) = brush_render_resource(&**brush) else { return };
74        if !self.resources_hash_set.insert(Rc::as_ptr(brush) as *const () as usize) {
75            return;
76        }
77        resource.add_ref_on_compositor(compositor);
78        self.resources.push(RecordedResource::Brush(brush.clone()));
79    }
80
81    fn add_pen_resource(&mut self, pen: Option<&Rc<dyn IPen>>) {
82        let (Some(compositor), Some(pen)) = (&self.compositor, pen) else { return };
83        if is_immutable_pen(&**pen) {
84            return;
85        }
86        let Some(resource) = pen_render_resource(&**pen) else { return };
87        if !self.resources_hash_set.insert(Rc::as_ptr(pen) as *const () as usize) {
88            return;
89        }
90        resource.add_ref_on_compositor(compositor);
91        self.resources.push(RecordedResource::Pen(pen.clone()));
92    }
93
94    fn add_geometry_resource(&mut self, geometry: &Ref<Geometry>) {
95        let Some(compositor) = &self.compositor else { return };
96        let Some(resource) = geometry_render_resource(geometry) else { return };
97        if !self.resources_hash_set.insert(&**geometry as *const Geometry as usize) {
98            return;
99        }
100        resource.add_ref_on_compositor(compositor);
101        self.resources.push(RecordedResource::Geometry(geometry.clone()));
102    }
103
104    fn pushed_scope(&mut self, position_before: usize) {
105        let stream = self.stream();
106        let entry = PushEntry {
107            emitted: true,
108            position_before,
109            position_after: stream.opcode_length(),
110            depth_before: stream.depth() - 1,
111        };
112        self.push_stack.push(entry);
113    }
114
115    fn pushed_no_op_scope(&mut self) {
116        self.push_stack.push(PushEntry::default());
117    }
118
119    fn pop_core(&mut self) {
120        let entry = self.push_stack.pop().expect("a pop without a matching push");
121        if !entry.emitted {
122            return;
123        }
124        let stream = self.stream();
125        // Nothing was drawn inside the scope: drop the push instead of
126        // closing it.
127        if stream.opcode_length() == entry.position_after {
128            stream.rewind(entry.position_before, entry.depth_before);
129        } else {
130            stream.pop();
131        }
132    }
133
134    fn flush_stack(&mut self) {
135        while !self.push_stack.is_empty() {
136            self.pop_core();
137        }
138    }
139
140    /// Finishes the recording and returns the render data, registered for
141    /// serialization on the compositor; `None` if nothing was drawn. The
142    /// context is ready to record again.
143    ///
144    /// Panics if the context has no compositor.
145    pub fn get_render_results(&mut self) -> Option<Rc<CompositionRenderData>> {
146        let Some(compositor) = self.compositor.clone() else {
147            panic!("the drawing context records without a compositor");
148        };
149        self.flush_stack();
150
151        let resources = std::mem::take(&mut self.resources);
152        self.resources_hash_set.clear();
153        let stream = self.stream.take();
154        match stream {
155            Some(stream) if !resources.is_empty() || stream.opcode_length() > 0 => {
156                let render_data = CompositionRenderData::new(&compositor, stream, resources);
157                compositor.register_for_serialization(render_data.clone());
158                Some(render_data)
159            }
160            Some(mut stream) => {
161                stream.dispose();
162                None
163            }
164            None => None,
165        }
166    }
167
168    /// Finishes a compositor-less recording and returns what was recorded
169    /// as the content of the scene brush `brush`; `None` if nothing was
170    /// drawn. `rect` is the bounds of the content; without it the bounds of
171    /// what was drawn are used.
172    pub fn get_immediate_scene_brush_content(
173        &mut self,
174        brush: Rc<dyn ITileBrush>,
175        rect: Option<Rect>,
176        use_scalable_rasterization: bool,
177    ) -> Option<Rc<ImmediateRenderDataSceneBrushContent>> {
178        debug_assert!(self.compositor.is_none());
179        self.flush_stack();
180
181        match self.stream.take() {
182            Some(stream) if stream.opcode_length() > 0 => Some(Rc::new(ImmediateRenderDataSceneBrushContent::new(
183                brush,
184                stream,
185                rect,
186                use_scalable_rasterization,
187            ))),
188            Some(mut stream) => {
189                stream.dispose();
190                None
191            }
192            None => None,
193        }
194    }
195
196    /// Discards what has been recorded so far.
197    pub fn reset(&mut self) {
198        if let Some(mut stream) = self.stream.take() {
199            stream.dispose_resources();
200            stream.dispose();
201        }
202        if let Some(compositor) = &self.compositor {
203            for resource in self.resources.drain(..) {
204                resource.with_resource(|r| r.release_on_compositor(compositor));
205            }
206        }
207        self.resources.clear();
208        self.push_stack.clear();
209        self.resources_hash_set.clear();
210    }
211}
212
213impl IDrawingContextCore for RenderDataDrawingContext {
214    fn draw_line_core(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point) {
215        self.add_pen_resource(Some(pen));
216        let compositor = self.compositor.clone();
217        let server_pen = pen_get_server(Some(pen), compositor.as_deref());
218        let client_pen = pen_get_client(Some(pen), compositor.as_deref());
219        self.stream().draw_line(server_pen, client_pen, p1, p2);
220    }
221
222    fn draw_geometry_impl_core(
223        &mut self,
224        brush: Option<&Rc<dyn IBrush>>,
225        pen: Option<&Rc<dyn IPen>>,
226        geometry: &Arc<dyn IGeometryImpl>,
227    ) {
228        if brush.is_none() && pen.is_none() {
229            return;
230        }
231        self.add_brush_resource(brush);
232        self.add_pen_resource(pen);
233        let compositor = self.compositor.clone();
234        let server_brush = brush_get_server(brush, compositor.as_deref());
235        let server_pen = pen_get_server(pen, compositor.as_deref());
236        let client_pen = pen_get_client(pen, compositor.as_deref());
237        self.stream().draw_geometry(
238            server_brush,
239            server_pen,
240            client_pen,
241            Some(super::RenderDataResource::GeometryImpl(geometry.clone())),
242        );
243    }
244
245    fn draw_geometry_core(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, geometry: &Ref<Geometry>) {
246        if brush.is_none() && pen.is_none() {
247            return;
248        }
249        self.add_brush_resource(brush);
250        self.add_pen_resource(pen);
251        self.add_geometry_resource(geometry);
252        let compositor = self.compositor.clone();
253        let server_brush = brush_get_server(brush, compositor.as_deref());
254        let server_pen = pen_get_server(pen, compositor.as_deref());
255        let client_pen = pen_get_client(pen, compositor.as_deref());
256        let geometry = geometry_get_server(geometry, compositor.as_deref());
257        self.stream().draw_geometry(server_brush, server_pen, client_pen, geometry);
258    }
259
260    fn draw_rectangle_core(
261        &mut self,
262        brush: Option<&Rc<dyn IBrush>>,
263        pen: Option<&Rc<dyn IPen>>,
264        rrect: RoundedRect,
265        box_shadows: &BoxShadows,
266    ) {
267        if rrect.is_empty() {
268            return;
269        }
270        if brush.is_none() && pen.is_none() && *box_shadows == BoxShadows::default() {
271            return;
272        }
273        self.add_brush_resource(brush);
274        self.add_pen_resource(pen);
275        let compositor = self.compositor.clone();
276        let server_brush = brush_get_server(brush, compositor.as_deref());
277        let server_pen = pen_get_server(pen, compositor.as_deref());
278        let client_pen = pen_get_client(pen, compositor.as_deref());
279        self.stream().draw_rectangle(server_brush, server_pen, client_pen, rrect, box_shadows);
280    }
281
282    fn draw_ellipse_core(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect) {
283        if rect.is_empty() {
284            return;
285        }
286        if brush.is_none() && pen.is_none() {
287            return;
288        }
289        self.add_brush_resource(brush);
290        self.add_pen_resource(pen);
291        let compositor = self.compositor.clone();
292        let server_brush = brush_get_server(brush, compositor.as_deref());
293        let server_pen = pen_get_server(pen, compositor.as_deref());
294        let client_pen = pen_get_client(pen, compositor.as_deref());
295        self.stream().draw_ellipse(server_brush, server_pen, client_pen, rect);
296    }
297
298    fn draw_bitmap(&mut self, source: &crate::utilities::RefCounted<crate::platform::SharedBitmapImpl>, opacity: f64, source_rect: Rect, dest_rect: Rect) {
299        if source_rect.is_empty() || dest_rect.is_empty() {
300            return;
301        }
302        // `source.Clone()`: the record has a counted reference of its own, so the bitmap outlives a caller
303        // that disposes its reference before the compositor plays the record.
304        self.stream().draw_bitmap(Some(source.clone_ref()), opacity, source_rect, dest_rect);
305    }
306
307    fn custom(&mut self, custom: &std::sync::Arc<dyn ICustomDrawOperation>) {
308        self.stream().draw_custom(Some(custom.clone()));
309    }
310
311    fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>) {
312        if foreground.is_none() {
313            return;
314        }
315        self.add_brush_resource(foreground);
316        let compositor = self.compositor.clone();
317        let server_brush = brush_get_server(foreground, compositor.as_deref());
318        self.stream().draw_glyph_run(server_brush, Some(glyph_run.platform_impl()));
319    }
320
321    fn push_clip_core(&mut self, rect: Rect) {
322        let before = self.stream().opcode_length();
323        self.stream().push_clip(RoundedRect::from_rect(rect));
324        self.pushed_scope(before);
325    }
326
327    fn push_rounded_clip_core(&mut self, rect: RoundedRect) {
328        let before = self.stream().opcode_length();
329        self.stream().push_clip(rect);
330        self.pushed_scope(before);
331    }
332
333    fn push_geometry_clip_core(&mut self, clip: &Ref<Geometry>) {
334        self.add_geometry_resource(clip);
335        let before = self.stream().opcode_length();
336        let compositor = self.compositor.clone();
337        let geometry = geometry_get_server(clip, compositor.as_deref());
338        self.stream().push_geometry_clip(geometry);
339        self.pushed_scope(before);
340    }
341
342    fn push_opacity_core(&mut self, opacity: f64) {
343        if opacity == 1.0 {
344            self.pushed_no_op_scope();
345            return;
346        }
347        let before = self.stream().opcode_length();
348        self.stream().push_opacity(opacity);
349        self.pushed_scope(before);
350    }
351
352    fn push_opacity_mask_core(&mut self, mask: &Rc<dyn IBrush>, bounds: Rect) {
353        self.add_brush_resource(Some(mask));
354        let before = self.stream().opcode_length();
355        let compositor = self.compositor.clone();
356        let server_brush = brush_get_server(Some(mask), compositor.as_deref());
357        self.stream().push_opacity_mask(server_brush, bounds);
358        self.pushed_scope(before);
359    }
360
361    fn push_transform_core(&mut self, matrix: Matrix) {
362        if matrix.is_identity() {
363            self.pushed_no_op_scope();
364            return;
365        }
366        let before = self.stream().opcode_length();
367        self.stream().push_transform(matrix);
368        self.pushed_scope(before);
369    }
370
371    fn push_render_options_core(&mut self, render_options: RenderOptions) {
372        let before = self.stream().opcode_length();
373        self.stream().push_render_options(render_options);
374        self.pushed_scope(before);
375    }
376
377    fn push_text_options_core(&mut self, text_options: TextOptions) {
378        let before = self.stream().opcode_length();
379        self.stream().push_text_options(text_options);
380        self.pushed_scope(before);
381    }
382
383    fn push_effect_core(&mut self, effect: &Rc<dyn IEffect>, bounds: Rect) {
384        let before = self.stream().opcode_length();
385        let padding = EffectExtensions::get_effect_output_padding(Some(&**effect));
386        let effect = EffectExtensions::to_immutable(effect);
387        self.stream().push_effect(Some(effect), bounds.inflate_thickness(padding));
388        self.pushed_scope(before);
389    }
390
391    fn pop_clip_core(&mut self) {
392        self.pop_core();
393    }
394
395    fn pop_geometry_clip_core(&mut self) {
396        self.pop_core();
397    }
398
399    fn pop_opacity_core(&mut self) {
400        self.pop_core();
401    }
402
403    fn pop_opacity_mask_core(&mut self) {
404        self.pop_core();
405    }
406
407    fn pop_transform_core(&mut self) {
408        self.pop_core();
409    }
410
411    fn pop_render_options_core(&mut self) {
412        self.pop_core();
413    }
414
415    fn pop_text_options_core(&mut self) {
416        self.pop_core();
417    }
418
419    fn pop_effect_core(&mut self) {
420        self.pop_core();
421    }
422
423    fn dispose_core(&mut self) {
424        self.reset();
425    }
426}