1use crate::media::immutable::ImmutableSolidColorBrush;
37use crate::media::text_formatting::ITextDrawingSink;
38use crate::media::IEffect;
39use crate::media::{
40 BoxShadows, FormattedText, Geometry, GlyphRun, IBrush, IExperimentalAcrylicMaterial, IImage, IPen, RenderOptions,
41 TextOptions,
42};
43use crate::platform::IGeometryImpl;
44use crate::rendering::scene_graph::ICustomDrawOperation;
45use crate::utilities::MathUtilities;
46use crate::{Matrix, Point, Rect, Ref, RoundedRect};
47use std::cell::RefCell;
48use std::ops::{Deref, DerefMut};
49use std::rc::Rc;
50use std::sync::Arc;
51
52pub trait IDrawingContextCore {
57 fn draw_line_core(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point);
59
60 fn draw_geometry_core(
63 &mut self,
64 brush: Option<&Rc<dyn IBrush>>,
65 pen: Option<&Rc<dyn IPen>>,
66 geometry: &Ref<Geometry>,
67 ) {
68 if let Some(geometry_impl) = geometry.platform_impl() {
69 self.draw_geometry_impl_core(brush, pen, &geometry_impl);
70 }
71 }
72
73 fn draw_geometry_impl_core(
75 &mut self,
76 brush: Option<&Rc<dyn IBrush>>,
77 pen: Option<&Rc<dyn IPen>>,
78 geometry: &Arc<dyn IGeometryImpl>,
79 );
80
81 fn draw_rectangle_core(
83 &mut self,
84 brush: Option<&Rc<dyn IBrush>>,
85 pen: Option<&Rc<dyn IPen>>,
86 rrect: RoundedRect,
87 box_shadows: &BoxShadows,
88 );
89
90 fn draw_ellipse_core(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect);
92
93 fn draw_bitmap(&mut self, source: &crate::utilities::RefCounted<crate::platform::SharedBitmapImpl>, opacity: f64, source_rect: Rect, dest_rect: Rect);
96
97 fn custom(&mut self, custom: &std::sync::Arc<dyn ICustomDrawOperation>);
99
100 fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>);
102
103 fn draw_rectangle_acrylic_core(&mut self, material: &dyn IExperimentalAcrylicMaterial, rect: RoundedRect) {
111 let brush: Rc<dyn IBrush> = Rc::new(ImmutableSolidColorBrush::new(material.fallback_color()));
112 self.draw_rectangle_core(Some(&brush), None, rect, &BoxShadows::default());
113 }
114
115 fn push_clip_core(&mut self, rect: Rect);
116 fn push_rounded_clip_core(&mut self, rect: RoundedRect);
117 fn push_geometry_clip_core(&mut self, clip: &Ref<Geometry>);
118 fn push_opacity_core(&mut self, opacity: f64);
119 fn push_opacity_mask_core(&mut self, mask: &Rc<dyn IBrush>, bounds: Rect);
120 fn push_transform_core(&mut self, matrix: Matrix);
121 fn push_render_options_core(&mut self, render_options: RenderOptions);
122 fn push_text_options_core(&mut self, text_options: TextOptions);
123 fn push_effect_core(&mut self, effect: &Rc<dyn IEffect>, bounds: Rect);
124
125 fn pop_clip_core(&mut self);
126 fn pop_geometry_clip_core(&mut self);
127 fn pop_opacity_core(&mut self);
128 fn pop_opacity_mask_core(&mut self);
129 fn pop_transform_core(&mut self);
130 fn pop_render_options_core(&mut self);
131 fn pop_text_options_core(&mut self);
132 fn pop_effect_core(&mut self);
133
134 fn dispose_core(&mut self);
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq)]
141enum PushedStateType {
142 Transform,
143 Opacity,
144 Clip,
145 GeometryClip,
146 OpacityMask,
147 RenderOptions,
148 TextOptions,
149 Effect,
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155#[must_use = "a pushed state must be popped with `DrawingContext::pop` (or left to the context's disposal)"]
156pub struct PushedState {
157 level: usize,
159}
160
161impl PushedState {
162 pub const NONE: PushedState = PushedState { level: 0 };
165}
166
167impl Default for PushedState {
168 fn default() -> Self {
169 Self::NONE
170 }
171}
172
173enum CoreRef<'a> {
174 Borrowed(&'a mut dyn IDrawingContextCore),
175 Owned(Box<dyn IDrawingContextCore + 'a>),
176}
177
178crate::ferro_thread_local! {
179 static STATE_STACK_POOL: RefCell<Vec<Vec<PushedStateType>>> = const { RefCell::new(Vec::new()) };
180}
181
182const STATE_STACK_POOL_LIMIT: usize = 16;
183
184pub struct DrawingContext<'a> {
186 core: CoreRef<'a>,
187 states: Option<Vec<PushedStateType>>,
188 disposed: bool,
189}
190
191impl<'a> DrawingContext<'a> {
192 pub fn new(core: &'a mut dyn IDrawingContextCore) -> Self {
195 Self { core: CoreRef::Borrowed(core), states: None, disposed: false }
196 }
197
198 pub fn owned(core: Box<dyn IDrawingContextCore + 'a>) -> Self {
201 Self { core: CoreRef::Owned(core), states: None, disposed: false }
202 }
203
204 #[inline]
205 fn core(&mut self) -> &mut dyn IDrawingContextCore {
206 match &mut self.core {
207 CoreRef::Borrowed(core) => &mut **core,
208 CoreRef::Owned(core) => &mut **core,
209 }
210 }
211
212 pub fn dispose(mut self) {
215 self.dispose_in_place();
216 }
217
218 fn dispose_in_place(&mut self) {
219 if self.disposed {
220 return;
221 }
222 self.disposed = true;
223 if let Some(mut states) = self.states.take() {
224 while let Some(state) = states.pop() {
225 self.restore(state);
226 }
227 STATE_STACK_POOL.with(|pool| {
228 let mut pool = pool.borrow_mut();
229 if pool.len() < STATE_STACK_POOL_LIMIT {
230 pool.push(states);
231 }
232 });
233 }
234 if let CoreRef::Owned(core) = &mut self.core {
235 core.dispose_core();
236 }
237 }
238
239 pub fn draw_image(&mut self, source: &dyn IImage, rect: Rect) {
241 self.draw_image_with_rects(source, Rect::from_size(source.size()), rect);
242 }
243
244 pub fn draw_image_with_rects(&mut self, source: &dyn IImage, source_rect: Rect, dest_rect: Rect) {
246 source.draw(self, source_rect, dest_rect);
247 }
248
249 pub fn draw_bitmap(&mut self, source: &crate::utilities::RefCounted<crate::platform::SharedBitmapImpl>, opacity: f64, source_rect: Rect, dest_rect: Rect) {
251 self.core().draw_bitmap(source, opacity, source_rect, dest_rect);
252 }
253
254 pub fn draw_line(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point) {
256 if pen_is_visible(Some(pen)) {
257 self.core().draw_line_core(pen, p1, p2);
258 }
259 }
260
261 pub fn draw_geometry(
263 &mut self,
264 brush: Option<&Rc<dyn IBrush>>,
265 pen: Option<&Rc<dyn IPen>>,
266 geometry: &Ref<Geometry>,
267 ) {
268 if brush.is_some() || pen_is_visible(pen) {
269 self.core().draw_geometry_core(brush, pen, geometry);
270 }
271 }
272
273 pub fn draw_geometry_impl(
275 &mut self,
276 brush: Option<&Rc<dyn IBrush>>,
277 pen: Option<&Rc<dyn IPen>>,
278 geometry: &Arc<dyn IGeometryImpl>,
279 ) {
280 if brush.is_some() || pen_is_visible(pen) {
281 self.core().draw_geometry_impl_core(brush, pen, geometry);
282 }
283 }
284
285 pub fn draw_rectangle(
291 &mut self,
292 brush: Option<&Rc<dyn IBrush>>,
293 pen: Option<&Rc<dyn IPen>>,
294 rect: Rect,
295 mut radius_x: f64,
296 mut radius_y: f64,
297 box_shadows: &BoxShadows,
298 ) {
299 if brush.is_none() && !pen_is_visible(pen) && box_shadows.count() == 0 {
300 return;
301 }
302 if !MathUtilities::is_zero(radius_x) {
303 radius_x = radius_x.min(rect.width / 2.0);
304 }
305 if !MathUtilities::is_zero(radius_y) {
306 radius_y = radius_y.min(rect.height / 2.0);
307 }
308 self.core().draw_rectangle_core(brush, pen, RoundedRect::from_radius_xy(rect, radius_x, radius_y), box_shadows);
309 }
310
311 pub fn draw_rounded_rectangle(
313 &mut self,
314 brush: Option<&Rc<dyn IBrush>>,
315 pen: Option<&Rc<dyn IPen>>,
316 rrect: RoundedRect,
317 box_shadows: &BoxShadows,
318 ) {
319 if brush.is_none() && !pen_is_visible(pen) && box_shadows.count() == 0 {
320 return;
321 }
322 self.core().draw_rectangle_core(brush, pen, rrect, box_shadows);
323 }
324
325 pub fn draw_rectangle_outline(&mut self, pen: &Rc<dyn IPen>, rect: Rect, corner_radius: f32) {
327 let radius = f64::from(corner_radius);
328 self.draw_rectangle(None, Some(pen), rect, radius, radius, &BoxShadows::default());
329 }
330
331 pub fn fill_rectangle(&mut self, brush: &Rc<dyn IBrush>, rect: Rect, corner_radius: f32) {
333 let radius = f64::from(corner_radius);
334 self.draw_rectangle(Some(brush), None, rect, radius, radius, &BoxShadows::default());
335 }
336
337 pub fn draw_ellipse_at(
339 &mut self,
340 brush: Option<&Rc<dyn IBrush>>,
341 pen: Option<&Rc<dyn IPen>>,
342 center: Point,
343 radius_x: f64,
344 radius_y: f64,
345 ) {
346 if brush.is_some() || pen_is_visible(pen) {
347 let origin_x = center.x - radius_x;
348 let origin_y = center.y - radius_y;
349 let width = radius_x * 2.0;
350 let height = radius_y * 2.0;
351 self.core().draw_ellipse_core(brush, pen, Rect::new(origin_x, origin_y, width, height));
352 }
353 }
354
355 pub fn draw_ellipse(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect) {
357 if brush.is_some() || pen_is_visible(pen) {
358 self.core().draw_ellipse_core(brush, pen, rect);
359 }
360 }
361
362 pub fn custom(&mut self, custom: &std::sync::Arc<dyn ICustomDrawOperation>) {
364 self.core().custom(custom);
365 }
366
367 pub fn draw_text(&mut self, text: &FormattedText, origin: Point) {
369 text.draw(self, origin);
370 }
371
372 pub fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>) {
374 self.core().draw_glyph_run(foreground, glyph_run);
375 }
376
377 pub fn draw_rectangle_acrylic(&mut self, material: &dyn IExperimentalAcrylicMaterial, rect: RoundedRect) {
382 self.core().draw_rectangle_acrylic_core(material, rect);
383 }
384
385 fn pushed(&mut self, state: PushedStateType) -> PushedState {
388 let states = self
389 .states
390 .get_or_insert_with(|| STATE_STACK_POOL.with(|pool| pool.borrow_mut().pop()).unwrap_or_default());
391 states.push(state);
392 PushedState { level: states.len() }
393 }
394
395 fn restore(&mut self, state: PushedStateType) {
396 let core = self.core();
397 match state {
398 PushedStateType::Transform => core.pop_transform_core(),
399 PushedStateType::Clip => core.pop_clip_core(),
400 PushedStateType::Opacity => core.pop_opacity_core(),
401 PushedStateType::GeometryClip => core.pop_geometry_clip_core(),
402 PushedStateType::OpacityMask => core.pop_opacity_mask_core(),
403 PushedStateType::RenderOptions => core.pop_render_options_core(),
404 PushedStateType::TextOptions => core.pop_text_options_core(),
405 PushedStateType::Effect => core.pop_effect_core(),
406 }
407 }
408
409 pub fn pop(&mut self, state: PushedState) {
415 if state.level == 0 {
416 return;
417 }
418 let Some(states) = self.states.as_mut() else { return };
419 if states.len() != state.level {
420 panic!("Wrong Push/Pop state order");
421 }
422 let restored = states.pop().expect("level is non-zero");
423 self.restore(restored);
424 }
425
426 pub fn scope(&mut self, state: PushedState) -> PushedScope<'_, 'a> {
429 PushedScope { context: self, state }
430 }
431
432 pub fn push_rounded_clip(&mut self, clip: RoundedRect) -> PushedState {
434 self.core().push_rounded_clip_core(clip);
435 self.pushed(PushedStateType::Clip)
436 }
437
438 pub fn push_clip(&mut self, clip: Rect) -> PushedState {
440 self.core().push_clip_core(clip);
441 self.pushed(PushedStateType::Clip)
442 }
443
444 pub fn push_geometry_clip(&mut self, clip: &Ref<Geometry>) -> PushedState {
446 self.core().push_geometry_clip_core(clip);
447 self.pushed(PushedStateType::GeometryClip)
448 }
449
450 pub fn push_opacity(&mut self, opacity: f64) -> PushedState {
452 self.core().push_opacity_core(opacity);
453 self.pushed(PushedStateType::Opacity)
454 }
455
456 pub fn push_opacity_mask(&mut self, mask: &Rc<dyn IBrush>, bounds: Rect) -> PushedState {
461 self.core().push_opacity_mask_core(mask, bounds);
462 self.pushed(PushedStateType::OpacityMask)
463 }
464
465 pub fn push_transform(&mut self, matrix: Matrix) -> PushedState {
467 self.core().push_transform_core(matrix);
468 self.pushed(PushedStateType::Transform)
469 }
470
471 pub fn push_render_options(&mut self, render_options: RenderOptions) -> PushedState {
473 self.core().push_render_options_core(render_options);
474 self.pushed(PushedStateType::RenderOptions)
475 }
476
477 pub fn push_text_options(&mut self, text_options: TextOptions) -> PushedState {
479 self.core().push_text_options_core(text_options);
480 self.pushed(PushedStateType::TextOptions)
481 }
482
483 pub fn push_effect(&mut self, effect: &Rc<dyn IEffect>, bounds: Rect) -> PushedState {
486 self.core().push_effect_core(effect, bounds);
487 self.pushed(PushedStateType::Effect)
488 }
489}
490
491impl ITextDrawingSink for DrawingContext<'_> {
496 fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>) {
497 DrawingContext::draw_glyph_run(self, foreground, glyph_run);
498 }
499
500 fn draw_rectangle(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect) {
501 DrawingContext::draw_rectangle(self, brush, pen, rect, 0.0, 0.0, &BoxShadows::default());
502 }
503
504 fn draw_line(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point) {
505 DrawingContext::draw_line(self, pen, p1, p2);
506 }
507
508 fn push_transform(&mut self, matrix: Matrix) {
509 let _ = DrawingContext::push_transform(self, matrix);
510 }
511
512 fn pop_transform(&mut self) {
513 let Some(states) = self.states.as_mut() else { return };
514 match states.pop() {
515 Some(PushedStateType::Transform) => self.restore(PushedStateType::Transform),
516 Some(_) => panic!("Wrong Push/Pop state order"),
517 None => {}
518 }
519 }
520}
521
522impl Drop for DrawingContext<'_> {
523 fn drop(&mut self) {
524 if !std::thread::panicking() {
527 self.dispose_in_place();
528 }
529 }
530}
531
532pub struct PushedScope<'c, 'a> {
535 context: &'c mut DrawingContext<'a>,
536 state: PushedState,
537}
538
539impl<'a> Deref for PushedScope<'_, 'a> {
540 type Target = DrawingContext<'a>;
541
542 fn deref(&self) -> &Self::Target {
543 self.context
544 }
545}
546
547impl DerefMut for PushedScope<'_, '_> {
548 fn deref_mut(&mut self) -> &mut Self::Target {
549 self.context
550 }
551}
552
553impl Drop for PushedScope<'_, '_> {
554 fn drop(&mut self) {
555 if !std::thread::panicking() {
556 self.context.pop(self.state);
557 }
558 }
559}
560
561pub(crate) fn pen_is_visible(pen: Option<&Rc<dyn IPen>>) -> bool {
563 pen.is_some_and(|pen| pen.brush().is_some() && pen.thickness() > 0.0)
564}