Skip to main content

ferroui_base/media/
platform_drawing_context.rs

1use super::drawing_context::IDrawingContextCore;
2use super::immediate_drawing_context::ImmediateDrawingContext;
3use crate::media::immutable::ImmutableSolidColorBrush;
4use crate::media::{
5    BoxShadows, EffectExtensions, Geometry, GlyphRun, IBrush, IEffect, IExperimentalAcrylicMaterial, IPen,
6    RenderOptions, TextOptions,
7};
8use crate::platform::{IDrawingContextImpl, IGeometryImpl};
9use crate::rendering::scene_graph::ICustomDrawOperation;
10use crate::{Matrix, Point, Rect, Ref, RoundedRect};
11use std::cell::RefCell;
12use std::rc::Rc;
13use std::sync::Arc;
14
15/// A platform drawing context implementation, borrowed or owned.
16pub(crate) enum ImplRef<'a> {
17    Borrowed(&'a mut dyn IDrawingContextImpl),
18    Owned(Box<dyn IDrawingContextImpl + 'a>),
19}
20
21impl<'a> ImplRef<'a> {
22    #[inline]
23    pub(crate) fn get(&mut self) -> &mut (dyn IDrawingContextImpl + 'a) {
24        match self {
25            ImplRef::Borrowed(value) => reborrow_impl(value),
26            ImplRef::Owned(value) => &mut **value,
27        }
28    }
29
30    pub(crate) fn is_owned(&self) -> bool {
31        matches!(self, ImplRef::Owned(_))
32    }
33}
34
35/// Reborrows the borrowed implementation for the duration of one call.
36#[inline]
37fn reborrow_impl<'s, 'a>(value: &'s mut &'a mut dyn IDrawingContextImpl) -> &'s mut (dyn IDrawingContextImpl + 'a) {
38    &mut **value
39}
40
41crate::ferro_thread_local! {
42    static TRANSFORM_STACK_POOL: RefCell<Vec<Vec<Matrix>>> = const { RefCell::new(Vec::new()) };
43}
44
45const TRANSFORM_STACK_POOL_LIMIT: usize = 16;
46
47/// The drawing context core that draws straight to a platform drawing
48/// context implementation.
49pub struct PlatformDrawingContext<'a> {
50    platform_impl: ImplRef<'a>,
51    transforms: Option<Vec<Matrix>>,
52}
53
54impl<'a> PlatformDrawingContext<'a> {
55    /// Creates a core that draws to `platform_impl` and disposes it when the
56    /// core is disposed (`ownsImpl: true`).
57    pub fn new(platform_impl: Box<dyn IDrawingContextImpl + 'a>) -> Self {
58        Self { platform_impl: ImplRef::Owned(platform_impl), transforms: None }
59    }
60
61    /// Creates a core that draws to `platform_impl` without taking ownership
62    /// of it (`ownsImpl: false`).
63    pub fn borrowed(platform_impl: &'a mut dyn IDrawingContextImpl) -> Self {
64        Self { platform_impl: ImplRef::Borrowed(platform_impl), transforms: None }
65    }
66
67    /// The platform drawing context implementation.
68    pub fn platform_impl(&mut self) -> &mut (dyn IDrawingContextImpl + 'a) {
69        self.platform_impl.get()
70    }
71
72    /// Draws a rectangle filled with an acrylic-like material. A backend
73    /// without acrylic support gets the rectangle filled with the fallback
74    /// color of the material.
75    pub fn draw_rectangle_acrylic(&mut self, material: &dyn IExperimentalAcrylicMaterial, rect: RoundedRect) {
76        let platform_impl = self.platform_impl.get();
77        if let Some(acrylic_impl) = platform_impl.as_drawing_context_with_acrylic_like_support() {
78            acrylic_impl.draw_rectangle_with_material(material, rect);
79        } else {
80            let brush = ImmutableSolidColorBrush::new(material.fallback_color());
81            platform_impl.draw_rectangle(Some(&brush), None, rect, &BoxShadows::default());
82        }
83    }
84}
85
86impl IDrawingContextCore for PlatformDrawingContext<'_> {
87    fn draw_line_core(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point) {
88        self.platform_impl.get().draw_line(Some(&**pen), p1, p2);
89    }
90
91    fn draw_geometry_impl_core(
92        &mut self,
93        brush: Option<&Rc<dyn IBrush>>,
94        pen: Option<&Rc<dyn IPen>>,
95        geometry: &Arc<dyn IGeometryImpl>,
96    ) {
97        self.platform_impl.get().draw_geometry(brush.map(|b| &**b), pen.map(|p| &**p), &**geometry);
98    }
99
100    fn draw_rectangle_core(
101        &mut self,
102        brush: Option<&Rc<dyn IBrush>>,
103        pen: Option<&Rc<dyn IPen>>,
104        rrect: RoundedRect,
105        box_shadows: &BoxShadows,
106    ) {
107        self.platform_impl.get().draw_rectangle(brush.map(|b| &**b), pen.map(|p| &**p), rrect, box_shadows);
108    }
109
110    fn draw_ellipse_core(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect) {
111        self.platform_impl.get().draw_ellipse(brush.map(|b| &**b), pen.map(|p| &**p), rect);
112    }
113
114    fn draw_bitmap(&mut self, source: &crate::utilities::RefCounted<crate::platform::SharedBitmapImpl>, opacity: f64, source_rect: Rect, dest_rect: Rect) {
115        self.platform_impl.get().draw_bitmap(&*source.item(), opacity, source_rect, dest_rect);
116    }
117
118    fn custom(&mut self, custom: &std::sync::Arc<dyn ICustomDrawOperation>) {
119        let mut immediate = ImmediateDrawingContext::borrowed(self.platform_impl.get());
120        custom.render(&mut immediate);
121        immediate.dispose();
122    }
123
124    fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>) {
125        if let Some(foreground) = foreground {
126            self.platform_impl.get().draw_glyph_run(Some(&**foreground), &*glyph_run.platform_impl());
127        }
128    }
129
130    fn draw_rectangle_acrylic_core(&mut self, material: &dyn IExperimentalAcrylicMaterial, rect: RoundedRect) {
131        self.draw_rectangle_acrylic(material, rect);
132    }
133
134    fn push_clip_core(&mut self, rect: Rect) {
135        self.platform_impl.get().push_clip(rect);
136    }
137
138    fn push_rounded_clip_core(&mut self, rect: RoundedRect) {
139        self.platform_impl.get().push_clip_rounded(rect);
140    }
141
142    fn push_geometry_clip_core(&mut self, clip: &Ref<Geometry>) {
143        let Some(platform_impl) = clip.platform_impl() else {
144            panic!("the clip geometry has no platform implementation");
145        };
146        self.platform_impl.get().push_geometry_clip(&*platform_impl);
147    }
148
149    fn push_opacity_core(&mut self, opacity: f64) {
150        self.platform_impl.get().push_opacity(opacity, None);
151    }
152
153    fn push_opacity_mask_core(&mut self, mask: &Rc<dyn IBrush>, bounds: Rect) {
154        self.platform_impl.get().push_opacity_mask(&**mask, bounds);
155    }
156
157    fn push_transform_core(&mut self, matrix: Matrix) {
158        let transforms = self
159            .transforms
160            .get_or_insert_with(|| TRANSFORM_STACK_POOL.with(|pool| pool.borrow_mut().pop()).unwrap_or_default());
161        let platform_impl = self.platform_impl.get();
162        let current = platform_impl.transform();
163        transforms.push(current);
164        platform_impl.set_transform(matrix * current);
165    }
166
167    fn push_render_options_core(&mut self, render_options: RenderOptions) {
168        self.platform_impl.get().push_render_options(render_options);
169    }
170
171    fn push_text_options_core(&mut self, text_options: TextOptions) {
172        self.platform_impl.get().push_text_options(text_options);
173    }
174
175    fn push_effect_core(&mut self, effect: &Rc<dyn IEffect>, bounds: Rect) {
176        if let Some(effect_impl) = self.platform_impl.get().as_drawing_context_impl_with_effects() {
177            let padding = EffectExtensions::get_effect_output_padding(Some(&**effect));
178            effect_impl.push_effect(Some(bounds.inflate_thickness(padding)), &**effect);
179        }
180    }
181
182    fn pop_clip_core(&mut self) {
183        self.platform_impl.get().pop_clip();
184    }
185
186    fn pop_geometry_clip_core(&mut self) {
187        self.platform_impl.get().pop_geometry_clip();
188    }
189
190    fn pop_opacity_core(&mut self) {
191        self.platform_impl.get().pop_opacity();
192    }
193
194    fn pop_opacity_mask_core(&mut self) {
195        self.platform_impl.get().pop_opacity_mask();
196    }
197
198    fn pop_transform_core(&mut self) {
199        let Some(transform) = self.transforms.as_mut().and_then(Vec::pop) else {
200            panic!("the drawing context has been disposed");
201        };
202        self.platform_impl.get().set_transform(transform);
203    }
204
205    fn pop_render_options_core(&mut self) {
206        self.platform_impl.get().pop_render_options();
207    }
208
209    fn pop_text_options_core(&mut self) {
210        self.platform_impl.get().pop_text_options();
211    }
212
213    fn pop_effect_core(&mut self) {
214        if let Some(effect_impl) = self.platform_impl.get().as_drawing_context_impl_with_effects() {
215            effect_impl.pop_effect();
216        }
217    }
218
219    fn dispose_core(&mut self) {
220        if self.platform_impl.is_owned() {
221            self.platform_impl.get().dispose();
222        }
223        if let Some(transforms) = self.transforms.take() {
224            if !transforms.is_empty() {
225                panic!("Not all states are disposed");
226            }
227            TRANSFORM_STACK_POOL.with(|pool| {
228                let mut pool = pool.borrow_mut();
229                if pool.len() < TRANSFORM_STACK_POOL_LIMIT {
230                    pool.push(transforms);
231                }
232            });
233        }
234    }
235}