1use std::{any::Any, sync::Arc};
2
3use zng_app::{
4 update::{EventUpdate, UPDATES},
5 widget::{
6 WIDGET,
7 node::{IntoUiNode, 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::{IntoVar, Var, WeakVar, 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<N>(&mut self, render_mode: RenderMode, scale_factor: Factor, mask: Option<ImageMaskMode>, render: N) -> ImageVar
40 where
41 N: FnOnce() -> UiNode + Send + Sync + 'static,
42 {
43 let scale_factor = scale_factor.into();
44 let result = var(Img::new_none(None));
45 let windows = self.render.windows();
46 self.render_img(
47 mask,
48 move || {
49 let node = render();
50 let r = windows.new_window_root(node, render_mode, scale_factor);
51 windows.enable_frame_capture_in_window_context(mask);
52 r
53 },
54 &result,
55 );
56 result.read_only()
57 }
58
59 pub(super) fn render_img<N>(&mut self, mask: Option<ImageMaskMode>, render: N, result: &Var<Img>)
60 where
61 N: FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send + Sync + 'static,
62 {
63 self.render.requests.push(RenderRequest {
64 render: Box::new(render),
65 image: result.downgrade(),
66 mask,
67 });
68 UPDATES.update(None);
69 }
70}
71
72impl ImageSource {
73 pub fn render<F, R>(new_img: F) -> Self
101 where
102 F: Fn(&ImageRenderArgs) -> R + Send + Sync + 'static,
103 R: ImageRenderWindowRoot,
104 {
105 let window = IMAGES_SV.read().render.windows();
106 Self::Render(
107 Arc::new(Box::new(move |args| {
108 if let Some(parent) = args.parent {
109 window.set_parent_in_window_context(parent);
110 }
111 let r = new_img(args);
112 window.enable_frame_capture_in_window_context(None);
113 Box::new(r)
114 })),
115 None,
116 )
117 }
118
119 pub fn render_node(render_mode: RenderMode, render: impl Fn(&ImageRenderArgs) -> UiNode + Send + Sync + 'static) -> Self {
151 let window = IMAGES_SV.read().render.windows();
152 Self::Render(
153 Arc::new(Box::new(move |args| {
154 if let Some(parent) = args.parent {
155 window.set_parent_in_window_context(parent);
156 }
157 let node = render(args);
158 window.enable_frame_capture_in_window_context(None);
159 window.new_window_root(node, render_mode, None)
160 })),
161 None,
162 )
163 }
164}
165
166impl IMAGES {
167 pub fn render<N, R>(&self, mask: Option<ImageMaskMode>, render: N) -> ImageVar
177 where
178 N: FnOnce() -> R + Send + Sync + 'static,
179 R: ImageRenderWindowRoot,
180 {
181 IMAGES_SV.write().render(mask, render)
182 }
183
184 pub fn render_node(
192 &self,
193 render_mode: RenderMode,
194 scale_factor: impl Into<Factor>,
195 mask: Option<ImageMaskMode>,
196 render: impl FnOnce() -> UiNode + Send + Sync + 'static,
197 ) -> ImageVar {
198 IMAGES_SV.write().render_node(render_mode, scale_factor.into(), mask, render)
199 }
200}
201
202#[expect(non_camel_case_types)]
204pub struct IMAGES_WINDOW;
205impl IMAGES_WINDOW {
206 pub fn hook_render_windows_service(&self, service: Box<dyn ImageRenderWindowsService>) {
210 let mut img = IMAGES_SV.write();
211 assert!(img.render.windows.is_none());
212 img.render.windows = Some(service);
213 }
214}
215
216impl ImageManager {
217 pub(super) fn update_render(&mut self) {
219 let mut images = IMAGES_SV.write();
220
221 if !images.render.active.is_empty() {
222 let windows = images.render.windows();
223
224 images.render.active.retain(|r| {
225 let mut retain = false;
226 if let Some(img) = r.image.upgrade() {
227 retain = img.with(Img::is_loading) || r.retain.get();
228 }
229
230 if !retain {
231 windows.close_window(r.window_id);
232 }
233
234 retain
235 });
236 }
237
238 if !images.render.requests.is_empty() {
239 let windows = images.render.windows();
240
241 for req in images.render.requests.drain(..) {
242 if let Some(img) = req.image.upgrade() {
243 let windows_in = windows.clone_boxed();
244 windows.open_headless_window(Box::new(move || {
245 let ctx = ImageRenderCtx::new();
246 let retain = ctx.retain.clone();
247 WINDOW.set_state(*IMAGE_RENDER_ID, ctx);
248
249 let w = (req.render)();
250
251 windows_in.enable_frame_capture_in_window_context(req.mask);
252
253 let a = ActiveRenderer {
254 window_id: WINDOW.id(),
255 image: img.downgrade(),
256 retain,
257 };
258 IMAGES_SV.write().render.active.push(a);
259
260 w
261 }));
262 }
263 }
264 }
265 }
266
267 pub(super) fn event_preview_render(&mut self, update: &EventUpdate) {
269 let imgs = IMAGES_SV.read();
270 if !imgs.render.active.is_empty()
271 && let Some((id, img)) = imgs.render.windows().on_frame_image_ready(update)
272 && let Some(a) = imgs.render.active.iter().find(|a| a.window_id == id)
273 && let Some(img_var) = a.image.upgrade()
274 {
275 img_var.set(img.clone());
276 }
277 }
278}
279
280#[derive(Default)]
282pub(super) struct ImagesRender {
283 windows: Option<Box<dyn ImageRenderWindowsService>>,
284 requests: Vec<RenderRequest>,
285 active: Vec<ActiveRenderer>,
286}
287impl ImagesRender {
288 fn windows(&self) -> Box<dyn ImageRenderWindowsService> {
289 self.windows.as_ref().expect("render windows service not inited").clone_boxed()
290 }
291}
292
293struct ActiveRenderer {
294 window_id: WindowId,
295 image: WeakVar<Img>,
296 retain: Var<bool>,
297}
298
299struct RenderRequest {
300 render: Box<dyn FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send + Sync>,
301 image: WeakVar<Img>,
302 mask: Option<ImageMaskMode>,
303}
304
305#[derive(Clone)]
306struct ImageRenderCtx {
307 retain: Var<bool>,
308}
309impl ImageRenderCtx {
310 fn new() -> Self {
311 Self { retain: var(false) }
312 }
313}
314
315static_id! {
316 static ref IMAGE_RENDER_ID: StateId<ImageRenderCtx>;
317}
318
319#[expect(non_camel_case_types)]
323pub struct IMAGE_RENDER;
324impl IMAGE_RENDER {
325 pub fn is_in_render(&self) -> bool {
329 WINDOW.contains_state(*IMAGE_RENDER_ID)
330 }
331
332 pub fn retain(&self) -> Var<bool> {
336 WINDOW.req_state(*IMAGE_RENDER_ID).retain
337 }
338}
339
340#[property(CONTEXT, default(false))]
348pub fn render_retain(child: impl IntoUiNode, retain: impl IntoVar<bool>) -> UiNode {
349 let retain = retain.into_var();
350 match_node(child, move |_, op| {
351 if let UiNodeOp::Init = op {
352 if IMAGE_RENDER.is_in_render() {
353 let actual_retain = IMAGE_RENDER.retain();
354 actual_retain.set_from(&retain);
355 let handle = actual_retain.bind(&retain);
356 WIDGET.push_var_handle(handle);
357 } else {
358 tracing::error!("can only set `render_retain` in render widgets")
359 }
360 }
361 })
362}
363
364pub trait ImageRenderWindowsService: Send + Sync + 'static {
368 fn clone_boxed(&self) -> Box<dyn ImageRenderWindowsService>;
370
371 fn new_window_root(&self, node: UiNode, render_mode: RenderMode, scale_factor: Option<Factor>) -> Box<dyn ImageRenderWindowRoot>;
373
374 fn set_parent_in_window_context(&self, parent_id: WindowId);
376
377 fn enable_frame_capture_in_window_context(&self, mask: Option<ImageMaskMode>);
385
386 fn open_headless_window(&self, new_window_root: Box<dyn FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send>);
392
393 fn on_frame_image_ready(&self, update: &EventUpdate) -> Option<(WindowId, Img)>;
395
396 fn close_window(&self, window_id: WindowId);
398}
399
400pub trait ImageRenderWindowRoot: Send + Any + 'static {
404 fn into_any(self: Box<Self>) -> Box<dyn Any>;
406}