Skip to main content

ferroui_base/rendering/composition/drawing/
render_data_stream.rs

1//! The recorded form of what a visual draws: opcodes followed by their
2//! payloads in a byte buffer ([`RenderDataWriter`]), with the resources
3//! they refer to in a table.
4
5use super::{
6    DrawBitmapPayload, DrawCustomPayload, DrawEllipsePayload, DrawGeometryPayload, DrawGlyphRunPayload,
7    DrawLinePayload, DrawRectanglePayload, IRenderDataVisitor, PushClipPayload,
8    PushEffectPayload, PushGeometryClipPayload, PushOpacityMaskPayload, PushOpacityPayload,
9    PushRenderOptionsPayload, PushTextOptionsPayload, PushTransformPayload, RenderDataOpcode, RenderDataReader,
10    RenderDataResource, RenderDataResources, RenderDataWriter,
11};
12use crate::media::{BoxShadow, BoxShadows, RenderOptions, TextOptions};
13use crate::platform::IGlyphRunImpl;
14use crate::rendering::composition::server::{IServerObject, ServerObjectId};
15use crate::rendering::composition::transport::{BatchObject, BatchStreamReader, BatchStreamWriter};
16use crate::rendering::scene_graph::ICustomDrawOperation;
17use crate::{Matrix, Point, Rect, RoundedRect};
18use std::rc::Rc;
19use std::sync::Arc;
20
21const TAG_NOT_SENT: u8 = 0;
22const TAG_BRUSH: u8 = 1;
23const TAG_PEN: u8 = 2;
24const TAG_GEOMETRY_IMPL: u8 = 3;
25// Tag 4 was a geometry wrapper sent by value, which does not happen.
26const TAG_GLYPH_RUN: u8 = 5;
27const TAG_BITMAP: u8 = 6;
28const TAG_CUSTOM: u8 = 7;
29const TAG_EFFECT: u8 = 8;
30const TAG_SERVER_BRUSH: u8 = 9;
31const TAG_SERVER_PEN: u8 = 10;
32const TAG_SERVER_GEOMETRY: u8 = 11;
33
34/// A recorded sequence of drawing operations with its resource table.
35#[derive(Default)]
36pub struct RenderDataStream {
37    writer: RenderDataWriter,
38    pub(super) resources: RenderDataResources,
39    depth: i32,
40    max_depth: i32,
41}
42
43impl RenderDataStream {
44    pub fn new() -> Self {
45        Self::default()
46    }
47
48    /// The recorded operations.
49    pub fn opcodes(&self) -> &[u8] {
50        self.writer.written()
51    }
52
53    /// The number of bytes recorded: the position a later
54    /// [`rewind`](Self::rewind) can return to.
55    pub fn opcode_length(&self) -> usize {
56        self.writer.length()
57    }
58
59    /// The current scope depth.
60    pub fn depth(&self) -> i32 {
61        self.depth
62    }
63
64    /// The deepest scope nesting recorded.
65    pub fn max_depth(&self) -> i32 {
66        self.max_depth
67    }
68
69    pub fn resource_count(&self) -> usize {
70        self.resources.count()
71    }
72
73    pub fn get_resource(&self, handle: i32) -> Option<&RenderDataResource> {
74        self.resources.get(handle)
75    }
76
77    /// Drops everything recorded after position `length` and restores the
78    /// scope depth.
79    pub fn rewind(&mut self, length: usize, depth: i32) {
80        self.writer.rewind(length);
81        self.depth = depth;
82    }
83
84    fn enter_scope(&mut self) {
85        self.depth += 1;
86        if self.depth > self.max_depth {
87            self.max_depth = self.depth;
88        }
89    }
90
91    /// Releases the resources the stream holds a reference to on behalf of
92    /// their owners.
93    pub fn dispose_resources(&self) {
94        for resource in self.resources.iter() {
95            if let RenderDataResource::CustomDrawOperation(operation) = resource {
96                operation.dispose();
97            }
98        }
99    }
100
101    pub fn draw_line(
102        &mut self,
103        server_pen: Option<RenderDataResource>,
104        client_pen: Option<RenderDataResource>,
105        p1: Point,
106        p2: Point,
107    ) {
108        let payload = DrawLinePayload {
109            server_pen: self.resources.intern(server_pen),
110            client_pen: self.resources.intern(client_pen),
111            p1,
112            p2,
113        };
114        self.writer.write_payload(payload);
115    }
116
117    pub fn draw_rectangle(
118        &mut self,
119        server_brush: Option<RenderDataResource>,
120        server_pen: Option<RenderDataResource>,
121        client_pen: Option<RenderDataResource>,
122        rect: RoundedRect,
123        box_shadows: &BoxShadows,
124    ) {
125        let payload = DrawRectanglePayload {
126            server_brush: self.resources.intern(server_brush),
127            server_pen: self.resources.intern(server_pen),
128            client_pen: self.resources.intern(client_pen),
129            rect,
130            box_shadow_count: box_shadows.count() as i32,
131        };
132        self.writer.write_payload(payload);
133        for shadow in box_shadows.iter() {
134            self.writer.write(shadow);
135        }
136    }
137
138    pub fn draw_ellipse(
139        &mut self,
140        server_brush: Option<RenderDataResource>,
141        server_pen: Option<RenderDataResource>,
142        client_pen: Option<RenderDataResource>,
143        rect: Rect,
144    ) {
145        let payload = DrawEllipsePayload {
146            server_brush: self.resources.intern(server_brush),
147            server_pen: self.resources.intern(server_pen),
148            client_pen: self.resources.intern(client_pen),
149            rect,
150        };
151        self.writer.write_payload(payload);
152    }
153
154    pub fn draw_geometry(
155        &mut self,
156        server_brush: Option<RenderDataResource>,
157        server_pen: Option<RenderDataResource>,
158        client_pen: Option<RenderDataResource>,
159        geometry: Option<RenderDataResource>,
160    ) {
161        let payload = DrawGeometryPayload {
162            server_brush: self.resources.intern(server_brush),
163            server_pen: self.resources.intern(server_pen),
164            client_pen: self.resources.intern(client_pen),
165            geometry: self.resources.intern(geometry),
166        };
167        self.writer.write_payload(payload);
168    }
169
170    /// Records a glyph run. The table entry is a new counted reference to
171    /// it (upstream's caller passes `PlatformImpl.Clone()`).
172    pub fn draw_glyph_run(
173        &mut self,
174        server_brush: Option<RenderDataResource>,
175        glyph_run: Option<std::sync::Arc<dyn IGlyphRunImpl>>,
176    ) {
177        let glyph_run = glyph_run.map(|glyph_run| RenderDataResource::GlyphRun(Arc::new(glyph_run)));
178        let payload = DrawGlyphRunPayload {
179            server_brush: self.resources.intern(server_brush),
180            glyph_run: self.resources.intern(glyph_run),
181        };
182        self.writer.write_payload(payload);
183    }
184
185    /// Records a bitmap. The table entry is a new counted reference to it
186    /// (upstream's caller passes `source.Clone()`).
187    pub fn draw_bitmap(
188        &mut self,
189        bitmap: Option<crate::utilities::RefCounted<crate::platform::SharedBitmapImpl>>,
190        opacity: f64,
191        source_rect: Rect,
192        dest_rect: Rect,
193    ) {
194        let bitmap = bitmap.map(|bitmap| RenderDataResource::Bitmap(Arc::new(bitmap)));
195        let payload = DrawBitmapPayload {
196            bitmap: self.resources.intern(bitmap),
197            opacity,
198            source_rect,
199            dest_rect,
200        };
201        self.writer.write_payload(payload);
202    }
203
204    pub fn draw_custom(&mut self, operation: Option<std::sync::Arc<dyn ICustomDrawOperation>>) {
205        let payload = DrawCustomPayload {
206            operation: self.resources.intern(operation.map(RenderDataResource::CustomDrawOperation)),
207        };
208        self.writer.write_payload(payload);
209    }
210
211    pub fn push_clip(&mut self, clip: RoundedRect) {
212        self.writer.write_payload(PushClipPayload { clip });
213        self.enter_scope();
214    }
215
216    pub fn push_geometry_clip(&mut self, geometry: Option<RenderDataResource>) {
217        let payload = PushGeometryClipPayload { geometry: self.resources.intern(geometry) };
218        self.writer.write_payload(payload);
219        self.enter_scope();
220    }
221
222    pub fn push_opacity(&mut self, opacity: f64) {
223        self.writer.write_payload(PushOpacityPayload { opacity });
224        self.enter_scope();
225    }
226
227    pub fn push_opacity_mask(&mut self, server_brush: Option<RenderDataResource>, bounds: Rect) {
228        let payload = PushOpacityMaskPayload { brush: self.resources.intern(server_brush), bounds };
229        self.writer.write_payload(payload);
230        self.enter_scope();
231    }
232
233    pub fn push_transform(&mut self, matrix: Matrix) {
234        self.writer.write_payload(PushTransformPayload { matrix });
235        self.enter_scope();
236    }
237
238    pub fn push_render_options(&mut self, options: RenderOptions) {
239        self.writer.write_payload(PushRenderOptionsPayload { options });
240        self.enter_scope();
241    }
242
243    pub fn push_text_options(&mut self, text_options: TextOptions) {
244        self.writer.write_payload(PushTextOptionsPayload { options: text_options });
245        self.enter_scope();
246    }
247
248    pub fn push_effect(&mut self, effect: Option<Arc<dyn crate::media::IImmutableEffect>>, bounds: Rect) {
249        let payload =
250            PushEffectPayload { effect: self.resources.intern(effect.map(RenderDataResource::Effect)), bounds };
251        self.writer.write_payload(payload);
252        self.enter_scope();
253    }
254
255    pub fn pop(&mut self) {
256        self.writer.write_opcode(RenderDataOpcode::Pop);
257        self.depth -= 1;
258    }
259
260    /// Writes the stream to a batch. Resources with a server-side
261    /// counterpart are sent as the counterpart's id; client-only resources
262    /// keep their slot but are not sent.
263    pub fn serialize_to(&self, writer: &mut BatchStreamWriter<'_>) {
264        writer.write(self.max_depth);
265        writer.write(self.resources.count() as i32);
266        for resource in self.resources.iter() {
267            match resource {
268                RenderDataResource::ClientPen(_) | RenderDataResource::NotSent => writer.write(TAG_NOT_SENT),
269                // A brush or a pen sent by value is an immutable one: what
270                // crosses to the render thread is its shared form.
271                RenderDataResource::Brush(v) => {
272                    writer.write(TAG_BRUSH);
273                    let shared = crate::media::shared_brush_of(&**v)
274                        .unwrap_or_else(|| panic!("The brush is not compatible with composition"));
275                    writer.write_object(BatchObject::value(shared));
276                }
277                RenderDataResource::Pen(v) => {
278                    writer.write(TAG_PEN);
279                    let shared = crate::media::shared_pen_of(&**v)
280                        .unwrap_or_else(|| panic!("The pen is not compatible with composition"));
281                    writer.write_object(BatchObject::value(shared));
282                }
283                RenderDataResource::GeometryImpl(v) => {
284                    writer.write(TAG_GEOMETRY_IMPL);
285                    writer.write_object(BatchObject::value(v.clone()));
286                }
287                // With a compositor a geometry object is sent as the id of
288                // its server object; the wrapper is what the server resolves
289                // the id to, and what render data keeps on the UI thread.
290                RenderDataResource::Geometry(_) => panic!("a geometry of render data is not sent by value"),
291                RenderDataResource::GlyphRun(v) => {
292                    writer.write(TAG_GLYPH_RUN);
293                    writer.write_object(BatchObject::value(v.clone()));
294                }
295                RenderDataResource::Bitmap(v) => {
296                    writer.write(TAG_BITMAP);
297                    writer.write_object(BatchObject::value(v.clone()));
298                }
299                RenderDataResource::CustomDrawOperation(v) => {
300                    writer.write(TAG_CUSTOM);
301                    writer.write_object(BatchObject::value(v.clone()));
302                }
303                RenderDataResource::Effect(v) => {
304                    writer.write(TAG_EFFECT);
305                    writer.write_object(BatchObject::value(v.clone()));
306                }
307                RenderDataResource::ServerBrush { server, .. } => {
308                    writer.write(TAG_SERVER_BRUSH);
309                    writer.write_server_object(Some(*server));
310                }
311                RenderDataResource::ServerPen { server, .. } => {
312                    writer.write(TAG_SERVER_PEN);
313                    writer.write_server_object(Some(*server));
314                }
315                RenderDataResource::ServerGeometry { server, .. } => {
316                    writer.write(TAG_SERVER_GEOMETRY);
317                    writer.write_server_object(Some(*server));
318                }
319            }
320        }
321        let opcodes = self.writer.written();
322        writer.write(opcodes.len() as i32);
323        writer.write_bytes(opcodes);
324    }
325
326    /// Reads a stream from a batch on the server. `resolve` maps the id of
327    /// a server object to the object.
328    pub fn deserialize_from(
329        &mut self,
330        reader: &mut BatchStreamReader<'_>,
331        resolve: &dyn Fn(ServerObjectId) -> Option<Rc<dyn IServerObject>>,
332    ) {
333        fn value<T: 'static>(reader: &mut BatchStreamReader<'_>) -> T {
334            match reader.read_value::<T>() {
335                Some(value) => value,
336                None => panic!("a render data resource is missing from the batch"),
337            }
338        }
339        fn server_object(
340            reader: &mut BatchStreamReader<'_>,
341            resolve: &dyn Fn(ServerObjectId) -> Option<Rc<dyn IServerObject>>,
342        ) -> Rc<dyn IServerObject> {
343            match reader.read_server_object().and_then(resolve) {
344                Some(object) => object,
345                None => panic!("render data refers to a server object that does not exist"),
346            }
347        }
348
349        self.max_depth = reader.read::<i32>();
350        let resource_count = reader.read::<i32>();
351        for _ in 0..resource_count {
352            let resource = match reader.read::<u8>() {
353                TAG_NOT_SENT => RenderDataResource::NotSent,
354                TAG_BRUSH => RenderDataResource::Brush(Rc::new(value::<crate::media::SharedBrush>(reader))),
355                TAG_PEN => RenderDataResource::Pen(Rc::new(value::<crate::media::SharedPen>(reader))),
356                TAG_GEOMETRY_IMPL => RenderDataResource::GeometryImpl(value(reader)),
357                TAG_GLYPH_RUN => RenderDataResource::GlyphRun(value(reader)),
358                TAG_BITMAP => RenderDataResource::Bitmap(value(reader)),
359                TAG_CUSTOM => RenderDataResource::CustomDrawOperation(value(reader)),
360                TAG_EFFECT => RenderDataResource::Effect(value(reader)),
361                TAG_SERVER_BRUSH => match server_object(reader, resolve).as_brush() {
362                    Some(brush) => RenderDataResource::Brush(brush),
363                    None => panic!("the server object referenced by render data is not a brush"),
364                },
365                TAG_SERVER_PEN => match server_object(reader, resolve).as_pen() {
366                    Some(pen) => RenderDataResource::Pen(pen),
367                    None => panic!("the server object referenced by render data is not a pen"),
368                },
369                TAG_SERVER_GEOMETRY => match server_object(reader, resolve).as_render_data_geometry() {
370                    Some(geometry) => RenderDataResource::Geometry(geometry),
371                    None => panic!("the server object referenced by render data is not a geometry"),
372                },
373                _ => panic!("unknown render data resource tag"),
374            };
375            self.resources.append_deserialized(resource);
376        }
377        let byte_count = reader.read::<i32>();
378        if byte_count > 0 {
379            reader.read_bytes(self.writer.reserve(byte_count as usize));
380        }
381    }
382
383    /// Walks the recorded operations.
384    pub fn visit<V: IRenderDataVisitor>(&self, visitor: &mut V) {
385        let mut scopes: Vec<V::Scope> = Vec::with_capacity(self.max_depth.max(0) as usize);
386        self.visit_with_scopes(visitor, &mut scopes);
387    }
388
389    /// Walks the recorded operations using a caller-provided scope stack,
390    /// which lets a caller that visits often reuse the allocation (upstream
391    /// keeps the scopes on the stack).
392    pub fn visit_with_scopes<V: IRenderDataVisitor>(&self, visitor: &mut V, scopes: &mut Vec<V::Scope>) {
393        let resources = &self.resources;
394        let mut reader = RenderDataReader::new(self.writer.written());
395        while !visitor.stop_visiting() && !reader.is_at_end() {
396            match reader.peek::<RenderDataOpcode>() {
397                RenderDataOpcode::DrawLine => {
398                    let p = reader.read_payload::<DrawLinePayload>();
399                    visitor.on_draw_line(resources.pen(p.server_pen), resources.pen(p.client_pen), p.p1, p.p2);
400                }
401                RenderDataOpcode::DrawRectangle => {
402                    let p = reader.read_payload::<DrawRectanglePayload>();
403                    let shadows = Self::read_box_shadows(&mut reader, p.box_shadow_count);
404                    visitor.on_draw_rectangle(
405                        resources.brush(p.server_brush),
406                        resources.pen(p.server_pen),
407                        resources.pen(p.client_pen),
408                        p.rect,
409                        &shadows,
410                    );
411                }
412                RenderDataOpcode::DrawEllipse => {
413                    let p = reader.read_payload::<DrawEllipsePayload>();
414                    visitor.on_draw_ellipse(
415                        resources.brush(p.server_brush),
416                        resources.pen(p.server_pen),
417                        resources.pen(p.client_pen),
418                        p.rect,
419                    );
420                }
421                RenderDataOpcode::DrawGeometry => {
422                    let p = reader.read_payload::<DrawGeometryPayload>();
423                    visitor.on_draw_geometry(
424                        resources.brush(p.server_brush),
425                        resources.pen(p.server_pen),
426                        resources.pen(p.client_pen),
427                        resources.geometry_impl(p.geometry).as_ref(),
428                    );
429                }
430                RenderDataOpcode::DrawGlyphRun => {
431                    let p = reader.read_payload::<DrawGlyphRunPayload>();
432                    visitor.on_draw_glyph_run(resources.brush(p.server_brush), resources.glyph_run(p.glyph_run));
433                }
434                RenderDataOpcode::DrawBitmap => {
435                    let p = reader.read_payload::<DrawBitmapPayload>();
436                    visitor.on_draw_bitmap(resources.bitmap(p.bitmap).as_ref(), p.opacity, p.source_rect, p.dest_rect);
437                }
438                RenderDataOpcode::DrawCustom => {
439                    let p = reader.read_payload::<DrawCustomPayload>();
440                    visitor.on_draw_custom(resources.custom_draw_operation(p.operation));
441                }
442                RenderDataOpcode::PushClip => {
443                    let p = reader.read_payload::<PushClipPayload>();
444                    scopes.push(visitor.on_push_clip(p.clip));
445                }
446                RenderDataOpcode::PushGeometryClip => {
447                    let p = reader.read_payload::<PushGeometryClipPayload>();
448                    scopes.push(visitor.on_push_geometry_clip(resources.geometry_impl(p.geometry).as_ref()));
449                }
450                RenderDataOpcode::PushOpacity => {
451                    let p = reader.read_payload::<PushOpacityPayload>();
452                    scopes.push(visitor.on_push_opacity(p.opacity));
453                }
454                RenderDataOpcode::PushOpacityMask => {
455                    let p = reader.read_payload::<PushOpacityMaskPayload>();
456                    scopes.push(visitor.on_push_opacity_mask(resources.brush(p.brush), p.bounds));
457                }
458                RenderDataOpcode::PushTransform => {
459                    let p = reader.read_payload::<PushTransformPayload>();
460                    scopes.push(visitor.on_push_transform(p.matrix));
461                }
462                RenderDataOpcode::PushRenderOptions => {
463                    let p = reader.read_payload::<PushRenderOptionsPayload>();
464                    scopes.push(visitor.on_push_render_options(p.options));
465                }
466                RenderDataOpcode::PushTextOptions => {
467                    let p = reader.read_payload::<PushTextOptionsPayload>();
468                    scopes.push(visitor.on_push_text_options(p.options));
469                }
470                RenderDataOpcode::PushEffect => {
471                    let p = reader.read_payload::<PushEffectPayload>();
472                    scopes.push(visitor.on_push_effect(resources.effect(p.effect), p.bounds));
473                }
474                RenderDataOpcode::Pop => {
475                    reader.read::<RenderDataOpcode>();
476                    let scope = scopes.pop().expect("a pop without a matching push in render data");
477                    visitor.on_pop(scope);
478                }
479                RenderDataOpcode::Invalid => panic!("render data holds an invalid opcode"),
480            }
481        }
482        scopes.clear();
483    }
484
485    fn read_box_shadows(reader: &mut RenderDataReader<'_>, count: i32) -> BoxShadows {
486        if count == 0 {
487            return BoxShadows::default();
488        }
489
490        let first = reader.read::<BoxShadow>();
491        if count == 1 {
492            return BoxShadows::new(first);
493        }
494
495        let rest: Vec<BoxShadow> = (1..count).map(|_| reader.read::<BoxShadow>()).collect();
496        BoxShadows::with_rest(first, &rest)
497    }
498
499    /// Releases the stream.
500    pub fn dispose(&mut self) {
501        self.writer.dispose();
502        self.resources.dispose();
503    }
504}