ferroui_base/rendering/composition/drawing/
render_data_drawing_context.rs1use 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
26pub(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
44pub 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 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 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 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 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 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 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}