ferroui_base/media/
platform_drawing_context.rs1use 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
15pub(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#[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
47pub struct PlatformDrawingContext<'a> {
50 platform_impl: ImplRef<'a>,
51 transforms: Option<Vec<Matrix>>,
52}
53
54impl<'a> PlatformDrawingContext<'a> {
55 pub fn new(platform_impl: Box<dyn IDrawingContextImpl + 'a>) -> Self {
58 Self { platform_impl: ImplRef::Owned(platform_impl), transforms: None }
59 }
60
61 pub fn borrowed(platform_impl: &'a mut dyn IDrawingContextImpl) -> Self {
64 Self { platform_impl: ImplRef::Borrowed(platform_impl), transforms: None }
65 }
66
67 pub fn platform_impl(&mut self) -> &mut (dyn IDrawingContextImpl + 'a) {
69 self.platform_impl.get()
70 }
71
72 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}