1use std::{any::Any, sync::Arc};
2
3use zng_app::{
4 update::{EventUpdate, UPDATES},
5 widget::{
6 WIDGET,
7 node::{BoxedUiNode, UiNode, UiNodeOp, match_node},
8 property,
9 },
10 window::{WINDOW, WindowId},
11};
12use zng_layout::unit::Factor;
13use zng_state_map::{StateId, static_id};
14use zng_var::{ArcVar, IntoVar, Var, WeakVar, types::WeakArcVar, var};
15use zng_view_api::{image::ImageMaskMode, window::RenderMode};
16
17use crate::{IMAGES, IMAGES_SV, ImageManager, ImageRenderArgs, ImageSource, ImageVar, ImagesService, Img};
18
19impl ImagesService {
20 fn render<N, R>(&mut self, mask: Option<ImageMaskMode>, render: N) -> ImageVar
21 where
22 N: FnOnce() -> R + Send + Sync + 'static,
23 R: ImageRenderWindowRoot,
24 {
25 let result = var(Img::new_none(None));
26 let windows = self.render.windows();
27 self.render_img(
28 mask,
29 move || {
30 let r = render();
31 windows.enable_frame_capture_in_window_context(None);
32 Box::new(r)
33 },
34 &result,
35 );
36 result.read_only()
37 }
38
39 fn render_node<U, N>(&mut self, render_mode: RenderMode, scale_factor: Factor, mask: Option<ImageMaskMode>, render: N) -> ImageVar
40 where
41 U: UiNode,
42 N: FnOnce() -> U + Send + Sync + 'static,
43 {
44 let scale_factor = scale_factor.into();
45 let result = var(Img::new_none(None));
46 let windows = self.render.windows();
47 self.render_img(
48 mask,
49 move || {
50 let node = render();
51 let r = windows.new_window_root(node.boxed(), render_mode, scale_factor);
52 windows.enable_frame_capture_in_window_context(mask);
53 r
54 },
55 &result,
56 );
57 result.read_only()
58 }
59
60 pub(super) fn render_img<N>(&mut self, mask: Option<ImageMaskMode>, render: N, result: &ArcVar<Img>)
61 where
62 N: FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send + Sync + 'static,
63 {
64 self.render.requests.push(RenderRequest {
65 render: Box::new(render),
66 image: result.downgrade(),
67 mask,
68 });
69 UPDATES.update(None);
70 }
71}
72
73impl ImageSource {
74 pub fn render<F, R>(new_img: F) -> Self
102 where
103 F: Fn(&ImageRenderArgs) -> R + Send + Sync + 'static,
104 R: ImageRenderWindowRoot,
105 {
106 let window = IMAGES_SV.read().render.windows();
107 Self::Render(
108 Arc::new(Box::new(move |args| {
109 if let Some(parent) = args.parent {
110 window.set_parent_in_window_context(parent);
111 }
112 let r = new_img(args);
113 window.enable_frame_capture_in_window_context(None);
114 Box::new(r)
115 })),
116 None,
117 )
118 }
119
120 pub fn render_node<U, N>(render_mode: RenderMode, render: N) -> Self
153 where
154 U: UiNode,
155 N: Fn(&ImageRenderArgs) -> U + Send + Sync + 'static,
156 {
157 let window = IMAGES_SV.read().render.windows();
158 Self::Render(
159 Arc::new(Box::new(move |args| {
160 if let Some(parent) = args.parent {
161 window.set_parent_in_window_context(parent);
162 }
163 let node = render(args);
164 window.enable_frame_capture_in_window_context(None);
165 window.new_window_root(node.boxed(), render_mode, None)
166 })),
167 None,
168 )
169 }
170}
171
172impl IMAGES {
173 pub fn render<N, R>(&self, mask: Option<ImageMaskMode>, render: N) -> ImageVar
183 where
184 N: FnOnce() -> R + Send + Sync + 'static,
185 R: ImageRenderWindowRoot,
186 {
187 IMAGES_SV.write().render(mask, render)
188 }
189
190 pub fn render_node<U, N>(
198 &self,
199 render_mode: RenderMode,
200 scale_factor: impl Into<Factor>,
201 mask: Option<ImageMaskMode>,
202 render: N,
203 ) -> ImageVar
204 where
205 U: UiNode,
206 N: FnOnce() -> U + Send + Sync + 'static,
207 {
208 IMAGES_SV.write().render_node(render_mode, scale_factor.into(), mask, render)
209 }
210}
211
212#[expect(non_camel_case_types)]
214pub struct IMAGES_WINDOW;
215impl IMAGES_WINDOW {
216 pub fn hook_render_windows_service(&self, service: Box<dyn ImageRenderWindowsService>) {
220 let mut img = IMAGES_SV.write();
221 assert!(img.render.windows.is_none());
222 img.render.windows = Some(service);
223 }
224}
225
226impl ImageManager {
227 pub(super) fn update_render(&mut self) {
229 let mut images = IMAGES_SV.write();
230
231 if !images.render.active.is_empty() {
232 let windows = images.render.windows();
233
234 images.render.active.retain(|r| {
235 let mut retain = false;
236 if let Some(img) = r.image.upgrade() {
237 retain = img.with(Img::is_loading) || r.retain.get();
238 }
239
240 if !retain {
241 windows.close_window(r.window_id);
242 }
243
244 retain
245 });
246 }
247
248 if !images.render.requests.is_empty() {
249 let windows = images.render.windows();
250
251 for req in images.render.requests.drain(..) {
252 if let Some(img) = req.image.upgrade() {
253 let windows_in = windows.clone_boxed();
254 windows.open_headless_window(Box::new(move || {
255 let ctx = ImageRenderCtx::new();
256 let retain = ctx.retain.clone();
257 WINDOW.set_state(*IMAGE_RENDER_ID, ctx);
258
259 let w = (req.render)();
260
261 windows_in.enable_frame_capture_in_window_context(req.mask);
262
263 let a = ActiveRenderer {
264 window_id: WINDOW.id(),
265 image: img.downgrade(),
266 retain,
267 };
268 IMAGES_SV.write().render.active.push(a);
269
270 w
271 }));
272 }
273 }
274 }
275 }
276
277 pub(super) fn event_preview_render(&mut self, update: &EventUpdate) {
279 let imgs = IMAGES_SV.read();
280 if !imgs.render.active.is_empty() {
281 if let Some((id, img)) = imgs.render.windows().on_frame_image_ready(update) {
282 if let Some(a) = imgs.render.active.iter().find(|a| a.window_id == id) {
283 if let Some(img_var) = a.image.upgrade() {
284 img_var.set(img.clone());
285 }
286 }
287 }
288 }
289 }
290}
291
292#[derive(Default)]
294pub(super) struct ImagesRender {
295 windows: Option<Box<dyn ImageRenderWindowsService>>,
296 requests: Vec<RenderRequest>,
297 active: Vec<ActiveRenderer>,
298}
299impl ImagesRender {
300 fn windows(&self) -> Box<dyn ImageRenderWindowsService> {
301 self.windows.as_ref().expect("render windows service not inited").clone_boxed()
302 }
303}
304
305struct ActiveRenderer {
306 window_id: WindowId,
307 image: WeakArcVar<Img>,
308 retain: ArcVar<bool>,
309}
310
311struct RenderRequest {
312 render: Box<dyn FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send + Sync>,
313 image: WeakArcVar<Img>,
314 mask: Option<ImageMaskMode>,
315}
316
317#[derive(Clone)]
318struct ImageRenderCtx {
319 retain: ArcVar<bool>,
320}
321impl ImageRenderCtx {
322 fn new() -> Self {
323 Self { retain: var(false) }
324 }
325}
326
327static_id! {
328 static ref IMAGE_RENDER_ID: StateId<ImageRenderCtx>;
329}
330
331#[expect(non_camel_case_types)]
335pub struct IMAGE_RENDER;
336impl IMAGE_RENDER {
337 pub fn is_in_render(&self) -> bool {
341 WINDOW.contains_state(*IMAGE_RENDER_ID)
342 }
343
344 pub fn retain(&self) -> ArcVar<bool> {
348 WINDOW.req_state(*IMAGE_RENDER_ID).retain
349 }
350}
351
352#[property(CONTEXT, default(false))]
360pub fn render_retain(child: impl UiNode, retain: impl IntoVar<bool>) -> impl UiNode {
361 let retain = retain.into_var();
362 match_node(child, move |_, op| {
363 if let UiNodeOp::Init = op {
364 if IMAGE_RENDER.is_in_render() {
365 let actual_retain = IMAGE_RENDER.retain();
366 actual_retain.set_from(&retain);
367 let handle = actual_retain.bind(&retain);
368 WIDGET.push_var_handle(handle);
369 } else {
370 tracing::error!("can only set `render_retain` in render widgets")
371 }
372 }
373 })
374}
375
376pub trait ImageRenderWindowsService: Send + Sync + 'static {
380 fn clone_boxed(&self) -> Box<dyn ImageRenderWindowsService>;
382
383 fn new_window_root(&self, node: BoxedUiNode, render_mode: RenderMode, scale_factor: Option<Factor>) -> Box<dyn ImageRenderWindowRoot>;
385
386 fn set_parent_in_window_context(&self, parent_id: WindowId);
388
389 fn enable_frame_capture_in_window_context(&self, mask: Option<ImageMaskMode>);
397
398 fn open_headless_window(&self, new_window_root: Box<dyn FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send>);
404
405 fn on_frame_image_ready(&self, update: &EventUpdate) -> Option<(WindowId, Img)>;
407
408 fn close_window(&self, window_id: WindowId);
410}
411
412pub trait ImageRenderWindowRoot: Send + Any + 'static {
416 fn into_any(self: Box<Self>) -> Box<dyn Any>;
418}