ferroui_base/rendering/composition/server/
server_composition_visual_surface.rs1use super::{
2 impl_server_render_resource, IAnimatedServerObject, IServerCompositionSurface, IServerObject,
3 IServerRenderResource, ServerCompositionSurfaceChanged, ServerCompositionVisual, ServerCompositor,
4 ServerRenderResource,
5};
6use crate::media::Colors;
7use crate::platform::{IDrawingContextImpl, IDrawingContextLayerImpl, LtrbRect};
8use crate::rendering::composition::generated::{
9 ServerCompositionVisualSurfaceHooks, ServerCompositionVisualSurfaceProps,
10};
11use crate::rendering::composition::transport::BatchStreamReader;
12use crate::{Matrix, PixelSize, Rect, Size, Vector};
13use std::any::{Any, TypeId};
14use std::cell::{Cell, RefCell};
15use std::rc::{Rc, Weak};
16use std::time::Duration;
17
18pub struct ServerCompositionVisualSurface {
37 base: ServerRenderResource,
38 props: ServerCompositionVisualSurfaceProps,
39 changed: ServerCompositionSurfaceChanged,
40 rendering: Cell<bool>,
43 freeze_requested: Cell<bool>,
44 frozen: Cell<bool>,
45 frozen_content: RefCell<Option<FrozenContent>>,
46 follows_source_bounds: Cell<bool>,
50 followed_source_bounds: Cell<Option<LtrbRect>>,
51}
52
53struct FrozenContent {
56 layer: Rc<dyn IDrawingContextLayerImpl>,
57 size: Size,
58}
59
60impl ServerCompositionVisualSurface {
61 pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionVisualSurface> {
64 let surface = Rc::new_cyclic(|this: &Weak<ServerCompositionVisualSurface>| ServerCompositionVisualSurface {
65 base: ServerRenderResource::new(
66 compositor,
67 this.clone() as Weak<dyn IAnimatedServerObject>,
68 this.clone() as Weak<dyn IServerRenderResource>,
69 ),
70 props: ServerCompositionVisualSurfaceProps::new(),
71 changed: ServerCompositionSurfaceChanged::new(),
72 rendering: Cell::new(false),
73 freeze_requested: Cell::new(false),
74 frozen: Cell::new(false),
75 frozen_content: RefCell::new(None),
76 follows_source_bounds: Cell::new(false),
77 followed_source_bounds: Cell::new(None),
78 });
79 surface.base.object().activate();
80 compositor.register_visual_surface(Rc::downgrade(&surface));
81 surface
82 }
83
84 pub fn props(&self) -> &ServerCompositionVisualSurfaceProps {
86 &self.props
87 }
88
89 pub fn is_disposed(&self) -> bool {
90 self.base.is_disposed()
91 }
92
93 pub fn is_frozen(&self) -> bool {
95 self.frozen.get()
96 }
97
98 pub fn source_visual(&self) -> Option<Rc<ServerCompositionVisual>> {
100 self.props.source_visual().and_then(|visual| visual.into_any_rc().downcast::<ServerCompositionVisual>().ok())
101 }
102
103 pub fn source_rect(&self) -> Option<Rect> {
108 let source = self.source_visual()?;
109 let offset = self.props.source_offset();
110 let mut size = self.props.source_size();
111 if size == Vector::default() {
112 size = source.size();
113 }
114 (size.x > 0.0 && size.y > 0.0).then(|| Rect::new(offset.x, offset.y, size.x, size.y))
115 }
116
117 pub fn request_freeze(&self) {
121 if !self.frozen.get() {
122 self.freeze_requested.set(true);
123 }
124 }
125
126 pub fn update_live_surfaces(
133 compositor: &ServerCompositor,
134 surfaces: &RefCell<Vec<Weak<ServerCompositionVisualSurface>>>,
135 ) -> bool {
136 let live: Vec<Rc<ServerCompositionVisualSurface>> = {
137 let mut surfaces = surfaces.borrow_mut();
138 surfaces.retain(|surface| surface.upgrade().is_some_and(|surface| !surface.is_disposed()));
139 surfaces.iter().filter_map(Weak::upgrade).collect()
140 };
141 let mut changed = false;
142 for surface in live {
143 if surface.freeze_requested.get() {
144 if compositor.is_rendering_stopped() || !compositor.render_interface().is_ready() {
146 continue;
147 }
148 surface.freeze(compositor);
149 changed = true;
150 } else if surface.source_changed() {
151 surface.content_changed();
152 changed = true;
153 }
154 }
155 changed
156 }
157
158 fn source_changed(&self) -> bool {
162 if self.frozen.get() {
163 return false;
164 }
165 let Some(source) = self.source_visual() else { return false };
166 if source.root().is_none() {
167 return false;
168 }
169 let mut changed = source.has_pending_render_changes();
170 if self.follows_source_bounds.get() {
171 let bounds = source.unclipped_sub_tree_bounds();
174 if self.followed_source_bounds.replace(bounds) != bounds {
175 changed = true;
176 }
177 }
178 changed
179 }
180
181 pub fn set_follows_source_bounds(&self, value: bool) {
185 self.follows_source_bounds.set(value);
186 }
187
188 pub fn render_source_subtree(&self, canvas: &mut dyn IDrawingContextImpl, clip: LtrbRect) {
195 let Some(source) = self.source_visual() else { return };
196 self.render_source(&source, canvas, clip);
197 }
198
199 fn content_changed(&self) {
200 self.base.core().invalidated(self);
201 self.changed.invoke();
202 }
203
204 pub fn freeze(&self, compositor: &ServerCompositor) {
211 self.freeze_requested.set(false);
212 if self.frozen.replace(true) {
213 return;
214 }
215 let (Some(source), Some(rect)) = (self.source_visual(), self.source_rect()) else {
216 self.content_changed();
217 return;
218 };
219 let scaling = source.root().map(|root| root.scaling()).filter(|scaling| *scaling > 0.0).unwrap_or(1.0);
220 let size = Size::new(rect.width, rect.height);
221 let pixel_size = PixelSize::from_size_ceiling(size, scaling);
222 if pixel_size.width < 1 || pixel_size.height < 1 {
223 self.content_changed();
224 return;
225 }
226
227 let current = compositor.render_interface().ensure_current();
228 let layer = compositor.render_interface().value().create_offscreen_render_target(
229 pixel_size,
230 Vector::new(scaling, scaling),
231 false,
232 );
233 {
234 let mut canvas = layer.create_drawing_context();
235 canvas.clear(Colors::TRANSPARENT);
236 canvas.set_transform(
237 Matrix::create_translation(-rect.x, -rect.y) * Matrix::create_scale(scaling, scaling),
238 );
239 self.render_source(&source, &mut *canvas, LtrbRect::INFINITE);
240 canvas.dispose();
241 }
242 current.dispose();
243 *self.frozen_content.borrow_mut() = Some(FrozenContent { layer, size });
244 self.content_changed();
245 }
246
247 fn render_source(&self, source: &Rc<ServerCompositionVisual>, canvas: &mut dyn IDrawingContextImpl, clip: LtrbRect) {
250 if self.rendering.replace(true) {
251 return;
252 }
253 source.render(canvas, clip, None, true, false, true);
256 self.rendering.set(false);
257 }
258
259 pub fn is_rendering(&self) -> bool {
262 self.rendering.get()
263 }
264
265 fn free_frozen_content(&self) {
266 if let Some(content) = self.frozen_content.borrow_mut().take() {
267 content.layer.dispose();
268 }
269 }
270}
271
272impl_server_render_resource!(
273 ServerCompositionVisualSurface,
274 base,
275 composition_property: ServerCompositionVisualSurfaceProps::get_composition_property
276);
277
278impl ServerCompositionVisualSurfaceHooks for ServerCompositionVisualSurface {
279 fn deserialize_changes_extra(&self, reader: &mut BatchStreamReader<'_>) {
282 if reader.read::<bool>() {
283 self.request_freeze();
284 }
285 }
286}
287
288impl IServerObject for ServerCompositionVisualSurface {
289 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
290 self.props.deserialize_changes_core(self, reader, committed_at);
291 }
292
293 fn values_invalidated(&self) {
294 self.content_changed();
295 }
296
297 fn dispose(&self) {
298 self.free_frozen_content();
299 self.base.core().dispose();
300 }
301
302 fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
303 Some(self)
304 }
305
306 fn as_surface(self: Rc<Self>) -> Option<Rc<dyn IServerCompositionSurface>> {
307 Some(self)
308 }
309
310 fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
311 Some(self)
312 }
313
314 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
315 self.props.find_props(type_id)
316 }
317
318 fn as_any(&self) -> &dyn Any {
319 self
320 }
321
322 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
323 self
324 }
325}
326
327impl IServerCompositionSurface for ServerCompositionVisualSurface {
328 fn bitmap(&self) -> Option<std::sync::Arc<crate::platform::SharedBitmapImpl>> {
331 None
332 }
333
334 fn changed(&self) -> &ServerCompositionSurfaceChanged {
335 &self.changed
336 }
337
338 fn natural_size(&self) -> Option<Size> {
339 if let Some(content) = self.frozen_content.borrow().as_ref() {
340 return Some(content.size);
341 }
342 if self.frozen.get() {
343 return None;
344 }
345 self.source_rect().map(|rect| Size::new(rect.width, rect.height))
346 }
347
348 fn draw(&self, canvas: &mut dyn IDrawingContextImpl, dest: Rect, opacity: f64) {
352 if dest.width <= 0.0 || dest.height <= 0.0 {
353 return;
354 }
355 if let Some(content) = self.frozen_content.borrow().as_ref() {
356 let pixel_size = content.layer.pixel_size();
357 canvas.draw_bitmap(
358 &*content.layer,
359 opacity,
360 Rect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64),
361 dest,
362 );
363 return;
364 }
365 if self.frozen.get() || self.rendering.get() {
366 return;
367 }
368 let (Some(source), Some(rect)) = (self.source_visual(), self.source_rect()) else { return };
369
370 let saved = canvas.transform();
371 canvas.push_clip(dest);
372 if opacity != 1.0 {
373 canvas.push_opacity(opacity, Some(dest));
374 }
375 let map = Matrix::create_translation(-rect.x, -rect.y)
376 * Matrix::create_scale(dest.width / rect.width, dest.height / rect.height)
377 * Matrix::create_translation(dest.x, dest.y);
378 canvas.set_transform(map * saved);
379 let clip = LtrbRect::from_rect(dest).transform_to_aabb(saved);
380 self.render_source(&source, canvas, clip);
381 canvas.set_transform(saved);
382 if opacity != 1.0 {
383 canvas.pop_opacity();
384 }
385 canvas.pop_clip();
386 }
387}