ferroui_xaml_interactions_custom/writeable_bitmap/
writeable_bitmap_render_behavior.rs1use crate::{IWriteableBitmapRenderer, WriteableBitmapRef};
4use ferroui_base::media::imaging::WriteableBitmap;
5use ferroui_base::platform::{AlphaFormat, PixelFormat};
6use ferroui_base::reactive::IDisposable;
7use ferroui_base::threading::DispatcherTimer;
8use ferroui_base::{
9 ferro_class, ferro_class_info, ferro_properties, instantiate, DirectProperty, FerroObjectImpl, FerroProperty,
10 PixelSize, Ref, StaticType, StyledElementImpl, StyledProperty, Vector,
11};
12use ferroui_controls::Image;
13use ferroui_xaml_interactivity::{StyledElementBehaviorImpl, StyledElementBehaviorOfT};
14use std::cell::RefCell;
15use std::rc::Rc;
16use std::time::Duration;
17
18#[repr(C)]
20pub struct WriteableBitmapRenderBehavior {
21 base: StyledElementBehaviorOfT,
22 timer: RefCell<Option<(Rc<DispatcherTimer>, Rc<dyn IDisposable>)>>,
23 bitmap: RefCell<Option<WriteableBitmapRef>>,
24}
25
26ferro_class!(WriteableBitmapRenderBehavior: StyledElementBehaviorOfT);
27ferro_class_info!(WriteableBitmapRenderBehavior { new: WriteableBitmapRenderBehavior::new });
28ferroui_base::ferro_impl_classes!(WriteableBitmapRenderBehavior: FerroObjectImpl, StyledElementImpl);
29
30ferro_properties! {
31 impl WriteableBitmapRenderBehavior {
32 pub fn pixel_width_property() -> StyledProperty<i32> {
34 FerroProperty::register::<WriteableBitmapRenderBehavior, _>("PixelWidth", 256)
35 }
36
37 pub fn pixel_height_property() -> StyledProperty<i32> {
39 FerroProperty::register::<WriteableBitmapRenderBehavior, _>("PixelHeight", 256)
40 }
41
42 pub fn renderer_property() -> StyledProperty<Option<Rc<dyn IWriteableBitmapRenderer>>> {
44 FerroProperty::register::<WriteableBitmapRenderBehavior, _>("Renderer", None)
45 }
46
47 pub fn bitmap_property() -> DirectProperty<WriteableBitmapRenderBehavior, Option<WriteableBitmapRef>> {
49 FerroProperty::register_direct::<WriteableBitmapRenderBehavior, _>(
50 "Bitmap",
51 |behavior| behavior.bitmap(),
52 None,
53 None,
54 )
55 }
56 }
57}
58
59ferroui_base::ferro_overrides! {
60 impl StyledElementBehaviorImpl for WriteableBitmapRenderBehavior {
61 fn on_attached_to_visual_tree(this: &Self) {
62 let bitmap = WriteableBitmapRef::new(WriteableBitmap::new(
63 PixelSize::new(this.pixel_width(), this.pixel_height()),
64 Vector::new(96.0, 96.0),
65 Some(PixelFormat::BGRA8888),
66 Some(AlphaFormat::Unpremul),
67 ));
68 this.set_bitmap(Some(bitmap.clone()));
69
70 if let Some(associated_object) = this.associated_object_as::<Image>() {
71 associated_object.set_source(Some(bitmap.as_image()));
72 }
73
74 let timer = DispatcherTimer::new();
75 timer.set_interval(Duration::from_millis(16));
76 let behavior = this.to_ref().downgrade();
77 let tick = timer.tick(move |_timer: &DispatcherTimer| {
78 if let Some(behavior) = behavior.upgrade() {
79 behavior.on_tick();
80 }
81 });
82 timer.start();
83 *this.timer.borrow_mut() = Some((timer, tick));
84 }
85
86 fn on_detached_from_visual_tree(this: &Self) {
87 let timer = this.timer.borrow_mut().take();
88 if let Some((timer, tick)) = timer {
89 tick.dispose();
90 timer.stop();
91 }
92
93 if let Some(associated_object) = this.associated_object_as::<Image>() {
94 associated_object.set_source(None);
95 }
96
97 if let Some(bitmap) = this.bitmap() {
98 bitmap.dispose();
99 }
100 this.set_bitmap(None);
101 }
102 }
103}
104
105impl WriteableBitmapRenderBehavior {
106 pub fn construct() -> Self {
108 Self {
109 base: StyledElementBehaviorOfT::construct(<Image as StaticType>::TYPE),
110 timer: RefCell::new(None),
111 bitmap: RefCell::new(None),
112 }
113 }
114
115 pub fn new() -> Ref<Self> {
117 instantiate(Self::construct())
118 }
119
120 pub fn pixel_width(&self) -> i32 {
122 self.get_value(Self::pixel_width_property())
123 }
124
125 pub fn set_pixel_width(&self, value: i32) {
126 self.set_value(Self::pixel_width_property(), value)
127 }
128
129 pub fn pixel_height(&self) -> i32 {
131 self.get_value(Self::pixel_height_property())
132 }
133
134 pub fn set_pixel_height(&self, value: i32) {
135 self.set_value(Self::pixel_height_property(), value)
136 }
137
138 pub fn renderer(&self) -> Option<Rc<dyn IWriteableBitmapRenderer>> {
141 self.get_value(Self::renderer_property())
142 }
143
144 pub fn set_renderer(&self, value: Option<Rc<dyn IWriteableBitmapRenderer>>) {
145 self.set_value(Self::renderer_property(), value)
146 }
147
148 pub fn bitmap(&self) -> Option<WriteableBitmapRef> {
150 self.bitmap.borrow().clone()
151 }
152
153 fn set_bitmap(&self, value: Option<WriteableBitmapRef>) {
154 self.set_and_raise(Self::bitmap_property(), &self.bitmap, value);
155 }
156
157 fn on_tick(&self) {
158 let (Some(bitmap), Some(renderer)) = (self.bitmap(), self.renderer()) else {
159 return;
160 };
161
162 renderer.render(&bitmap);
163 if let Some(associated_object) = self.associated_object_as::<Image>() {
164 associated_object.invalidate_visual();
165 }
166 }
167}