ferroui_base/media/imaging/
cropped_bitmap.rs1use crate::media::ref_adapter::RefAdapter;
2use crate::media::{DrawingContext, IAffectsRender, IImage};
3use crate::reactive::{Disposable, IDisposable};
4use crate::utilities::HandlerList;
5use crate::{
6 ferro_class, ferro_property, instantiate, FerroObject, FerroObjectImpl, FerroProperty,
7 FerroPropertyChangedEventArgs, PixelRect, Rect, Ref, Size, StyledProperty, Vector,
8};
9use std::any::Any;
10use std::rc::Rc;
11
12#[repr(C)]
14pub struct CroppedBitmap {
15 base: FerroObject,
16 invalidated: HandlerList<dyn Fn()>,
17}
18
19ferro_class! {
20 CroppedBitmap: FerroObject, virtuals CroppedBitmapImpl: FerroObjectImpl {
21 fn dispose(this);
23 }
24}
25crate::ferro_class_info!(CroppedBitmap { new: CroppedBitmap::new, interfaces: [std::rc::Rc<dyn crate::media::IImage>] });
26
27crate::ferro_impl_classes!(CroppedBitmap: FerroObjectImpl);
28
29impl CroppedBitmapImpl for CroppedBitmap {
30 fn dispose(this: &Self) {
31 if let Some(source) = this.source() {
32 if let Some(bitmap) = source.as_bitmap() {
33 bitmap.dispose();
34 }
35 }
36 }
37}
38
39crate::ferro_properties! { impl CroppedBitmap {
40 ferro_property!(
41 pub fn source_property() -> StyledProperty<Option<Rc<dyn IImage>>> {
43 FerroProperty::register::<CroppedBitmap, _>("Source", None)
44 }
45 );
46
47 ferro_property!(
48 pub fn source_rect_property() -> StyledProperty<PixelRect> {
50 FerroProperty::register::<CroppedBitmap, _>("SourceRect", PixelRect::default())
51 }
52 );
53} }
54
55impl CroppedBitmap {
56 fn static_constructor() {
57 Self::source_rect_property().changed().add_class_handler::<CroppedBitmap>(|x, e| x.source_rect_changed(e));
58 Self::source_property().changed().add_class_handler::<CroppedBitmap>(|x, e| x.source_changed(e));
59 }
60
61 pub fn construct() -> Self {
63 Self { base: FerroObject::construct(), invalidated: HandlerList::new() }
64 }
65
66 pub fn new() -> Ref<Self> {
67 instantiate(Self::construct())
68 }
69
70 pub fn with_source(source: Rc<dyn IImage>, source_rect: PixelRect) -> Ref<Self> {
72 let result = Self::new();
73 result.set_source(Some(source));
74 result.set_source_rect(source_rect);
75 result
76 }
77
78 pub fn invalidated(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
80 let token = self.invalidated.add(Rc::new(handler));
81 let weak = self.to_ref().downgrade();
82 Disposable::create(move || {
83 if let Some(this) = weak.upgrade() {
84 this.invalidated.remove(token);
85 }
86 })
87 }
88
89 fn raise_invalidated(&self) {
90 if self.invalidated.is_empty() {
91 return;
92 }
93 for (_, handler) in self.invalidated.snapshot().iter() {
94 handler();
95 }
96 }
97
98 pub fn source(&self) -> Option<Rc<dyn IImage>> {
100 self.get_value(Self::source_property())
101 }
102
103 pub fn set_source(&self, value: Option<Rc<dyn IImage>>) {
104 self.set_value(Self::source_property(), value)
105 }
106
107 pub fn source_rect(&self) -> PixelRect {
109 self.get_value(Self::source_rect_property())
110 }
111
112 pub fn set_source_rect(&self, value: PixelRect) {
113 self.set_value(Self::source_rect_property(), value)
114 }
115
116 fn source_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
117 let Some(new_value) = e.get_new_value::<Option<Rc<dyn IImage>>>() else { return };
118 if new_value.as_bitmap().is_none() {
119 panic!("Only IBitmap supported as source");
120 }
121 self.raise_invalidated();
122 }
123
124 fn source_rect_changed(&self, _e: &FerroPropertyChangedEventArgs<'_>) {
125 self.raise_invalidated();
126 }
127
128 pub fn size(&self) -> Size {
130 let Some(source) = self.source() else { return Size::default() };
131 let Some(bmp) = source.as_bitmap() else { return Size::default() };
132 let source_rect = self.source_rect();
133 if source_rect.width == 0 && source_rect.height == 0 {
134 return source.size();
135 }
136 source_rect.size().to_size_with_dpi_vector(bmp.dpi())
137 }
138
139 pub fn draw(&self, context: &mut DrawingContext<'_>, source_rect: Rect, dest_rect: Rect) {
142 let Some(source) = self.source() else { return };
143 let Some(bmp) = source.as_bitmap() else { return };
144 let top_left = self.source_rect().top_left().to_point_with_dpi_vector(bmp.dpi());
145 source.draw(context, source_rect.translate(Vector::new(top_left.x, top_left.y)), dest_rect);
146 }
147}
148
149impl IImage for RefAdapter<CroppedBitmap> {
150 fn size(&self) -> Size {
151 self.0.size()
152 }
153
154 fn draw(&self, context: &mut DrawingContext<'_>, source_rect: Rect, dest_rect: Rect) {
155 self.0.draw(context, source_rect, dest_rect)
156 }
157
158 fn as_any(&self) -> &dyn Any {
159 &*self.0
160 }
161
162 fn as_object(&self) -> Option<&FerroObject> {
163 Some(self.object())
164 }
165
166 fn as_affects_render(&self) -> Option<&dyn IAffectsRender> {
167 Some(&*self.0)
168 }
169
170 fn reference_id(&self) -> *const () {
171 RefAdapter::reference_id(self)
172 }
173}
174
175impl From<Ref<CroppedBitmap>> for Rc<dyn IImage> {
176 #[inline]
177 fn from(value: Ref<CroppedBitmap>) -> Self {
178 Rc::new(RefAdapter(value))
179 }
180}
181
182impl From<&Ref<CroppedBitmap>> for Rc<dyn IImage> {
183 #[inline]
184 fn from(value: &Ref<CroppedBitmap>) -> Self {
185 Rc::new(RefAdapter(value.clone()))
186 }
187}