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