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