pax_std_primitives/
image.rs1use pax_runtime::{api::Property, api::RenderContext, ExpandedNodeIdentifier};
2use pax_runtime_api::{borrow, borrow_mut, use_RefCell};
3use pax_std::{primitives::Image, types::ImageFit};
4use std::collections::HashMap;
5
6use_RefCell!();
7use pax_message::ImagePatch;
8use pax_runtime::{
9 BaseInstance, ExpandedNode, InstanceFlags, InstanceNode, InstantiationArgs, RuntimeContext,
10};
11use std::rc::Rc;
12
13use crate::patch_if_needed;
14pub struct ImageInstance {
17 base: BaseInstance,
18 native_message_props: RefCell<HashMap<ExpandedNodeIdentifier, Property<()>>>,
23}
24
25impl InstanceNode for ImageInstance {
26 fn instantiate(args: InstantiationArgs) -> Rc<Self>
27 where
28 Self: Sized,
29 {
30 Rc::new(Self {
31 base: BaseInstance::new(
32 args,
33 InstanceFlags {
34 invisible_to_slot: false,
35 invisible_to_raycasting: false,
36 layer: pax_runtime::api::Layer::Canvas,
37 is_component: false,
38 },
39 ),
40 native_message_props: Default::default(),
41 })
42 }
43
44 fn update(self: Rc<Self>, expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {
45 borrow!(self.native_message_props)
47 .get(&expanded_node.id)
48 .unwrap()
49 .get();
50 }
51
52 fn handle_mount(
53 self: Rc<Self>,
54 expanded_node: &Rc<ExpandedNode>,
55 context: &Rc<RuntimeContext>,
56 ) {
57 let id = expanded_node.id.to_u32();
58
59 let weak_self_ref = Rc::downgrade(&expanded_node);
61 let context = Rc::clone(context);
62 let last_patch = Rc::new(RefCell::new(ImagePatch {
63 id,
64 ..Default::default()
65 }));
66
67 let deps: Vec<_> = borrow!(expanded_node.properties_scope)
68 .values()
69 .cloned()
70 .chain([expanded_node.transform_and_bounds.untyped()])
71 .collect();
72 borrow_mut!(self.native_message_props).insert(
73 expanded_node.id,
74 Property::computed(
75 move || {
76 let Some(expanded_node) = weak_self_ref.upgrade() else {
77 unreachable!()
78 };
79 let mut old_state = borrow_mut!(last_patch);
80
81 let mut patch = ImagePatch {
82 id,
83 ..Default::default()
84 };
85 let path_val = expanded_node
86 .with_properties_unwrapped(|props: &mut Image| props.path.get().clone());
87
88 let update = patch_if_needed(&mut old_state.path, &mut patch.path, path_val);
89
90 if update {
91 context
92 .enqueue_native_message(pax_message::NativeMessage::ImageLoad(patch));
93 }
94 ()
95 },
96 &deps,
97 ),
98 );
99 }
100
101 fn handle_unmount(&self, expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {
102 let id = expanded_node.id.clone();
103 borrow_mut!(self.native_message_props).remove(&id);
105 }
106
107 fn render(
108 &self,
109 expanded_node: &ExpandedNode,
110 _rtc: &Rc<RuntimeContext>,
111 rc: &mut dyn RenderContext,
112 ) {
113 let t_and_b = expanded_node.transform_and_bounds.get();
114 let (container_width, container_height) = t_and_b.bounds;
115
116 expanded_node.with_properties_unwrapped(|props: &mut Image| {
117 let path = props.path.get();
118 let Some((image_width, image_height)) = rc.get_image_size(&path) else {
119 return;
121 };
122 let (image_width, image_height) = (image_width as f64, image_height as f64);
123 let stretch_w = container_width / image_width;
124 let stretch_h = container_height / image_height;
125 let (width, height) = match props.fit.get() {
126 ImageFit::FillVertical => (image_width * stretch_h, image_height * stretch_h),
127 ImageFit::FillHorizontal => (image_width * stretch_w, image_height * stretch_w),
128 ImageFit::Fill => {
129 let stretch = stretch_h.max(stretch_w);
130 (image_width * stretch, image_height * stretch)
131 }
132 ImageFit::Fit => {
133 let stretch = stretch_h.min(stretch_w);
134 (image_width * stretch, image_height * stretch)
135 }
136 ImageFit::Stretch => (container_width, container_height),
137 };
138 let x = (container_width - width) / 2.0;
139 let y = (container_height - height) / 2.0;
140 let transformed_bounds = kurbo::Rect::new(x, y, x + width, y + height);
141 let layer_id = format!("{}", borrow!(expanded_node.occlusion_id));
142 rc.save(&layer_id);
143 rc.transform(&layer_id, t_and_b.transform.into());
144 rc.draw_image(&layer_id, &path, transformed_bounds);
145 rc.restore(&layer_id);
146 });
147 }
148
149 fn base(&self) -> &BaseInstance {
150 &self.base
151 }
152
153 fn resolve_debug(
154 &self,
155 f: &mut std::fmt::Formatter,
156 _expanded_node: Option<&pax_runtime::ExpandedNode>,
157 ) -> std::fmt::Result {
158 f.debug_struct("Image").finish_non_exhaustive()
159 }
160}