Skip to main content

ferroui_base/media/
bitmap_cache.rs

1use crate::media::{CacheMode, CacheModeImpl};
2use crate::rendering::composition::{CompositionBitmapCache, CompositionCacheMode, Compositor};
3use crate::{
4    ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObjectImpl, FerroObjectImplExt,
5    FerroProperty, FerroPropertyChangedEventArgs, Ref, StyledElementImpl, StyledProperty,
6};
7use std::cell::RefCell;
8use std::rc::Rc;
9
10/// Creates and caches a bitmap representation of a visual.
11#[repr(C)]
12pub struct BitmapCache {
13    base: CacheMode,
14    current: RefCell<Option<Rc<CompositionBitmapCache>>>,
15}
16
17ferro_class!(BitmapCache: CacheMode);
18crate::ferro_class_info!(BitmapCache { new: BitmapCache::new });
19ferro_impl_classes!(BitmapCache: StyledElementImpl);
20
21impl FerroObjectImpl for BitmapCache {
22    fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
23        let current = this.current.borrow().clone();
24        if let (true, Some(current)) = (change.is_effective_value_change(), current) {
25            if change.property() == Self::render_at_scale_property().as_property() {
26                current.set_render_at_scale(this.render_at_scale());
27            } else if change.property() == Self::snaps_to_device_pixels_property().as_property() {
28                current.set_snaps_to_device_pixels(this.snaps_to_device_pixels());
29            } else if change.property() == Self::enable_clear_type_property().as_property() {
30                current.set_enable_clear_type(this.enable_clear_type());
31            }
32        }
33
34        Self::parent_on_property_changed(this, change);
35    }
36}
37
38impl CacheModeImpl for BitmapCache {
39    // We currently only allow visual to be attached to one compositor at a time, so keep it simple for now
40    fn get_for_compositor(this: &Self, c: &Rc<Compositor>) -> Rc<CompositionCacheMode> {
41        let current = this.current.borrow().clone();
42        match current {
43            Some(current) if Rc::ptr_eq(current.compositor(), c) => current,
44            _ => {
45                let current = CompositionBitmapCache::new(c);
46                current.set_enable_clear_type(this.enable_clear_type());
47                current.set_render_at_scale(this.render_at_scale());
48                current.set_snaps_to_device_pixels(this.snaps_to_device_pixels());
49                *this.current.borrow_mut() = Some(current.clone());
50                current
51            }
52        }
53    }
54}
55
56crate::ferro_properties! { impl BitmapCache {
57    ferro_property!(
58        /// Defines the `RenderAtScale` property.
59        pub fn render_at_scale_property() -> StyledProperty<f64> {
60            FerroProperty::register::<BitmapCache, _>("RenderAtScale", 1.0)
61        }
62    );
63
64    ferro_property!(
65        /// Defines the `SnapsToDevicePixels` property.
66        pub fn snaps_to_device_pixels_property() -> StyledProperty<bool> {
67            FerroProperty::register::<BitmapCache, _>("SnapsToDevicePixels", false)
68        }
69    );
70
71    ferro_property!(
72        /// Defines the `EnableClearType` property.
73        pub fn enable_clear_type_property() -> StyledProperty<bool> {
74            FerroProperty::register::<BitmapCache, _>("EnableClearType", false)
75        }
76    );
77} }
78
79impl BitmapCache {
80    /// Creates the class data.
81    pub fn construct() -> Self {
82        Self { base: CacheMode::construct(), current: RefCell::new(None) }
83    }
84
85    pub fn new() -> Ref<Self> {
86        instantiate(Self::construct())
87    }
88
89    /// The scale that is applied to the bitmap.
90    pub fn render_at_scale(&self) -> f64 {
91        self.get_value(Self::render_at_scale_property())
92    }
93
94    pub fn set_render_at_scale(&self, value: f64) {
95        self.set_value(Self::render_at_scale_property(), value)
96    }
97
98    /// Whether the bitmap is rendered with pixel snapping.
99    pub fn snaps_to_device_pixels(&self) -> bool {
100        self.get_value(Self::snaps_to_device_pixels_property())
101    }
102
103    pub fn set_snaps_to_device_pixels(&self, value: bool) {
104        self.set_value(Self::snaps_to_device_pixels_property(), value)
105    }
106
107    /// Whether the bitmap is rendered with ClearType activated.
108    pub fn enable_clear_type(&self) -> bool {
109        self.get_value(Self::enable_clear_type_property())
110    }
111
112    pub fn set_enable_clear_type(&self, value: bool) {
113        self.set_value(Self::enable_clear_type_property(), value)
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    // Not from upstream.
120    use super::*;
121
122    #[test]
123    fn defaults_and_parse() {
124        let cache = BitmapCache::new();
125        assert_eq!(1.0, cache.render_at_scale());
126        assert!(!cache.snaps_to_device_pixels());
127        assert!(!cache.enable_clear_type());
128        cache.set_render_at_scale(2.0);
129        assert_eq!(2.0, cache.render_at_scale());
130
131        assert!(CacheMode::parse("BitmapCache").unwrap().is::<BitmapCache>());
132        assert!(CacheMode::parse("bitmapcache").is_err());
133        assert!(CacheMode::parse("").is_err());
134    }
135}