Skip to main content

ferroui_base/media/imaging/
cropped_bitmap.rs

1use 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/// Crops a bitmap.
13#[repr(C)]
14pub struct CroppedBitmap {
15    base: FerroObject,
16    invalidated: HandlerList<dyn Fn()>,
17}
18
19ferro_class! {
20    CroppedBitmap: FerroObject, virtuals CroppedBitmapImpl: FerroObjectImpl {
21        /// Releases the source bitmap.
22        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        /// Defines the `Source` property.
42        pub fn source_property() -> StyledProperty<Option<Rc<dyn IImage>>> {
43            FerroProperty::register::<CroppedBitmap, _>("Source", None)
44        }
45    );
46
47    ferro_property!(
48        /// Defines the `SourceRect` property.
49        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    /// Creates the class data.
62    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    /// Creates a cropped bitmap showing `source_rect` of `source`.
71    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    /// Raised when the image changes.
79    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    /// The source for the bitmap.
99    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    /// The rectangular area that the bitmap is cropped to.
108    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    /// The size of the image, in device independent pixels.
129    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    /// Draws the cropped part of the source bitmap: `source_rect` is
140    /// relative to the top left corner of the crop rectangle.
141    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}