1use crate::media::immutable::ImmutableSolidColorBrush;
37use crate::media::text_formatting::ITextDrawingSink;
38use crate::media::IEffect;
39use crate::media::imaging::BitmapBlendingMode;
40use crate::media::{
41 BoxShadows, FormattedText, Geometry, GlyphRun, IBrush, IExperimentalAcrylicMaterial, IImage, IPen, RenderOptions,
42 TextOptions,
43};
44use crate::platform::IGeometryImpl;
45use crate::rendering::scene_graph::ICustomDrawOperation;
46use crate::utilities::MathUtilities;
47use crate::{Matrix, Point, Rect, Ref, RoundedRect};
48use std::cell::RefCell;
49use std::ops::{Deref, DerefMut};
50use std::rc::Rc;
51use std::sync::Arc;
52
53pub trait IDrawingContextCore {
58 fn draw_line_core(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point);
60
61 fn draw_geometry_core(
64 &mut self,
65 brush: Option<&Rc<dyn IBrush>>,
66 pen: Option<&Rc<dyn IPen>>,
67 geometry: &Ref<Geometry>,
68 ) {
69 if let Some(geometry_impl) = geometry.platform_impl() {
70 self.draw_geometry_impl_core(brush, pen, &geometry_impl);
71 }
72 }
73
74 fn draw_geometry_impl_core(
76 &mut self,
77 brush: Option<&Rc<dyn IBrush>>,
78 pen: Option<&Rc<dyn IPen>>,
79 geometry: &Arc<dyn IGeometryImpl>,
80 );
81
82 fn draw_rectangle_core(
84 &mut self,
85 brush: Option<&Rc<dyn IBrush>>,
86 pen: Option<&Rc<dyn IPen>>,
87 rrect: RoundedRect,
88 box_shadows: &BoxShadows,
89 );
90
91 fn draw_ellipse_core(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect);
93
94 fn draw_bitmap(&mut self, source: &crate::utilities::RefCounted<crate::platform::SharedBitmapImpl>, opacity: f64, source_rect: Rect, dest_rect: Rect);
97
98 fn custom(&mut self, custom: &std::sync::Arc<dyn ICustomDrawOperation>);
100
101 fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>);
103
104 fn draw_rectangle_acrylic_core(&mut self, material: &dyn IExperimentalAcrylicMaterial, rect: RoundedRect) {
112 let brush: Rc<dyn IBrush> = Rc::new(ImmutableSolidColorBrush::new(material.fallback_color()));
113 self.draw_rectangle_core(Some(&brush), None, rect, &BoxShadows::default());
114 }
115
116 fn push_clip_core(&mut self, rect: Rect);
117 fn push_rounded_clip_core(&mut self, rect: RoundedRect);
118 fn push_geometry_clip_core(&mut self, clip: &Ref<Geometry>);
119 fn push_opacity_core(&mut self, opacity: f64);
120 fn push_opacity_mask_core(&mut self, mask: &Rc<dyn IBrush>, bounds: Rect);
121 fn push_transform_core(&mut self, matrix: Matrix);
122 fn push_render_options_core(&mut self, render_options: RenderOptions);
123 fn push_text_options_core(&mut self, text_options: TextOptions);
124 fn push_effect_core(&mut self, effect: &Rc<dyn IEffect>, bounds: Rect);
125
126 fn try_push_compositing_layer_core(&mut self, _blend: BitmapBlendingMode, _opacity: f64, _bounds: Rect) -> bool {
128 false
129 }
130
131 fn pop_compositing_layer_core(&mut self) {
132 panic!("This drawing context does not support isolated compositing layers");
133 }
134
135 fn pop_clip_core(&mut self);
136 fn pop_geometry_clip_core(&mut self);
137 fn pop_opacity_core(&mut self);
138 fn pop_opacity_mask_core(&mut self);
139 fn pop_transform_core(&mut self);
140 fn pop_render_options_core(&mut self);
141 fn pop_text_options_core(&mut self);
142 fn pop_effect_core(&mut self);
143
144 fn dispose_core(&mut self);
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151enum PushedStateType {
152 Transform,
153 Opacity,
154 Clip,
155 GeometryClip,
156 OpacityMask,
157 RenderOptions,
158 TextOptions,
159 Effect,
160 CompositingLayer,
161}
162
163#[derive(Clone, Copy, Debug, PartialEq, Eq)]
166#[must_use = "a pushed state must be popped with `DrawingContext::pop` (or left to the context's disposal)"]
167pub struct PushedState {
168 level: usize,
170}
171
172impl PushedState {
173 pub const NONE: PushedState = PushedState { level: 0 };
176}
177
178impl Default for PushedState {
179 fn default() -> Self {
180 Self::NONE
181 }
182}
183
184enum CoreRef<'a> {
185 Borrowed(&'a mut dyn IDrawingContextCore),
186 Owned(Box<dyn IDrawingContextCore + 'a>),
187}
188
189crate::ferro_thread_local! {
190 static STATE_STACK_POOL: RefCell<Vec<Vec<PushedStateType>>> = const { RefCell::new(Vec::new()) };
191}
192
193const STATE_STACK_POOL_LIMIT: usize = 16;
194
195pub struct DrawingContext<'a> {
197 core: CoreRef<'a>,
198 states: Option<Vec<PushedStateType>>,
199 disposed: bool,
200}
201
202impl<'a> DrawingContext<'a> {
203 pub fn new(core: &'a mut dyn IDrawingContextCore) -> Self {
206 Self { core: CoreRef::Borrowed(core), states: None, disposed: false }
207 }
208
209 pub fn owned(core: Box<dyn IDrawingContextCore + 'a>) -> Self {
212 Self { core: CoreRef::Owned(core), states: None, disposed: false }
213 }
214
215 #[inline]
216 fn core(&mut self) -> &mut dyn IDrawingContextCore {
217 match &mut self.core {
218 CoreRef::Borrowed(core) => &mut **core,
219 CoreRef::Owned(core) => &mut **core,
220 }
221 }
222
223 pub fn dispose(mut self) {
226 self.dispose_in_place();
227 }
228
229 fn dispose_in_place(&mut self) {
230 if self.disposed {
231 return;
232 }
233 self.disposed = true;
234 if let Some(mut states) = self.states.take() {
235 while let Some(state) = states.pop() {
236 self.restore(state);
237 }
238 STATE_STACK_POOL.with(|pool| {
239 let mut pool = pool.borrow_mut();
240 if pool.len() < STATE_STACK_POOL_LIMIT {
241 pool.push(states);
242 }
243 });
244 }
245 if let CoreRef::Owned(core) = &mut self.core {
246 core.dispose_core();
247 }
248 }
249
250 pub fn draw_image(&mut self, source: &dyn IImage, rect: Rect) {
252 self.draw_image_with_rects(source, Rect::from_size(source.size()), rect);
253 }
254
255 pub fn draw_image_with_rects(&mut self, source: &dyn IImage, source_rect: Rect, dest_rect: Rect) {
257 source.draw(self, source_rect, dest_rect);
258 }
259
260 pub fn draw_bitmap(&mut self, source: &crate::utilities::RefCounted<crate::platform::SharedBitmapImpl>, opacity: f64, source_rect: Rect, dest_rect: Rect) {
262 self.core().draw_bitmap(source, opacity, source_rect, dest_rect);
263 }
264
265 pub fn draw_line(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point) {
267 if pen_is_visible(Some(pen)) {
268 self.core().draw_line_core(pen, p1, p2);
269 }
270 }
271
272 pub fn draw_geometry(
274 &mut self,
275 brush: Option<&Rc<dyn IBrush>>,
276 pen: Option<&Rc<dyn IPen>>,
277 geometry: &Ref<Geometry>,
278 ) {
279 if brush.is_some() || pen_is_visible(pen) {
280 self.core().draw_geometry_core(brush, pen, geometry);
281 }
282 }
283
284 pub fn draw_geometry_impl(
286 &mut self,
287 brush: Option<&Rc<dyn IBrush>>,
288 pen: Option<&Rc<dyn IPen>>,
289 geometry: &Arc<dyn IGeometryImpl>,
290 ) {
291 if brush.is_some() || pen_is_visible(pen) {
292 self.core().draw_geometry_impl_core(brush, pen, geometry);
293 }
294 }
295
296 pub fn draw_rectangle(
302 &mut self,
303 brush: Option<&Rc<dyn IBrush>>,
304 pen: Option<&Rc<dyn IPen>>,
305 rect: Rect,
306 mut radius_x: f64,
307 mut radius_y: f64,
308 box_shadows: &BoxShadows,
309 ) {
310 if brush.is_none() && !pen_is_visible(pen) && box_shadows.count() == 0 {
311 return;
312 }
313 if !MathUtilities::is_zero(radius_x) {
314 radius_x = radius_x.min(rect.width / 2.0);
315 }
316 if !MathUtilities::is_zero(radius_y) {
317 radius_y = radius_y.min(rect.height / 2.0);
318 }
319 self.core().draw_rectangle_core(brush, pen, RoundedRect::from_radius_xy(rect, radius_x, radius_y), box_shadows);
320 }
321
322 pub fn draw_rounded_rectangle(
324 &mut self,
325 brush: Option<&Rc<dyn IBrush>>,
326 pen: Option<&Rc<dyn IPen>>,
327 rrect: RoundedRect,
328 box_shadows: &BoxShadows,
329 ) {
330 if brush.is_none() && !pen_is_visible(pen) && box_shadows.count() == 0 {
331 return;
332 }
333 self.core().draw_rectangle_core(brush, pen, rrect, box_shadows);
334 }
335
336 pub fn draw_rectangle_outline(&mut self, pen: &Rc<dyn IPen>, rect: Rect, corner_radius: f32) {
338 let radius = f64::from(corner_radius);
339 self.draw_rectangle(None, Some(pen), rect, radius, radius, &BoxShadows::default());
340 }
341
342 pub fn fill_rectangle(&mut self, brush: &Rc<dyn IBrush>, rect: Rect, corner_radius: f32) {
344 let radius = f64::from(corner_radius);
345 self.draw_rectangle(Some(brush), None, rect, radius, radius, &BoxShadows::default());
346 }
347
348 pub fn draw_ellipse_at(
350 &mut self,
351 brush: Option<&Rc<dyn IBrush>>,
352 pen: Option<&Rc<dyn IPen>>,
353 center: Point,
354 radius_x: f64,
355 radius_y: f64,
356 ) {
357 if brush.is_some() || pen_is_visible(pen) {
358 let origin_x = center.x - radius_x;
359 let origin_y = center.y - radius_y;
360 let width = radius_x * 2.0;
361 let height = radius_y * 2.0;
362 self.core().draw_ellipse_core(brush, pen, Rect::new(origin_x, origin_y, width, height));
363 }
364 }
365
366 pub fn draw_ellipse(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect) {
368 if brush.is_some() || pen_is_visible(pen) {
369 self.core().draw_ellipse_core(brush, pen, rect);
370 }
371 }
372
373 pub fn custom(&mut self, custom: &std::sync::Arc<dyn ICustomDrawOperation>) {
375 self.core().custom(custom);
376 }
377
378 pub fn draw_text(&mut self, text: &FormattedText, origin: Point) {
380 text.draw(self, origin);
381 }
382
383 pub fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>) {
385 self.core().draw_glyph_run(foreground, glyph_run);
386 }
387
388 pub fn draw_rectangle_acrylic(&mut self, material: &dyn IExperimentalAcrylicMaterial, rect: RoundedRect) {
393 self.core().draw_rectangle_acrylic_core(material, rect);
394 }
395
396 fn pushed(&mut self, state: PushedStateType) -> PushedState {
399 let states = self
400 .states
401 .get_or_insert_with(|| STATE_STACK_POOL.with(|pool| pool.borrow_mut().pop()).unwrap_or_default());
402 states.push(state);
403 PushedState { level: states.len() }
404 }
405
406 fn restore(&mut self, state: PushedStateType) {
407 let core = self.core();
408 match state {
409 PushedStateType::Transform => core.pop_transform_core(),
410 PushedStateType::Clip => core.pop_clip_core(),
411 PushedStateType::Opacity => core.pop_opacity_core(),
412 PushedStateType::GeometryClip => core.pop_geometry_clip_core(),
413 PushedStateType::OpacityMask => core.pop_opacity_mask_core(),
414 PushedStateType::RenderOptions => core.pop_render_options_core(),
415 PushedStateType::TextOptions => core.pop_text_options_core(),
416 PushedStateType::Effect => core.pop_effect_core(),
417 PushedStateType::CompositingLayer => core.pop_compositing_layer_core(),
418 }
419 }
420
421 pub fn pop(&mut self, state: PushedState) {
427 if state.level == 0 {
428 return;
429 }
430 let Some(states) = self.states.as_mut() else { return };
431 if states.len() != state.level {
432 panic!("Wrong Push/Pop state order");
433 }
434 let restored = states.pop().expect("level is non-zero");
435 self.restore(restored);
436 }
437
438 pub fn scope(&mut self, state: PushedState) -> PushedScope<'_, 'a> {
441 PushedScope { context: self, state }
442 }
443
444 pub fn push_rounded_clip(&mut self, clip: RoundedRect) -> PushedState {
446 self.core().push_rounded_clip_core(clip);
447 self.pushed(PushedStateType::Clip)
448 }
449
450 pub fn push_clip(&mut self, clip: Rect) -> PushedState {
452 self.core().push_clip_core(clip);
453 self.pushed(PushedStateType::Clip)
454 }
455
456 pub fn push_geometry_clip(&mut self, clip: &Ref<Geometry>) -> PushedState {
458 self.core().push_geometry_clip_core(clip);
459 self.pushed(PushedStateType::GeometryClip)
460 }
461
462 pub fn push_opacity(&mut self, opacity: f64) -> PushedState {
464 self.core().push_opacity_core(opacity);
465 self.pushed(PushedStateType::Opacity)
466 }
467
468 pub fn try_push_compositing_layer(
481 &mut self,
482 blend: BitmapBlendingMode,
483 opacity: f64,
484 bounds: Rect,
485 ) -> Option<PushedState> {
486 if ![bounds.x, bounds.y, bounds.width, bounds.height, opacity].iter().all(|value| value.is_finite())
487 || bounds.width < 0.0 || bounds.height < 0.0 || !(0.0..=1.0).contains(&opacity)
488 {
489 return None;
490 }
491 if !self.core().try_push_compositing_layer_core(blend, opacity, bounds) {
492 return None;
493 }
494 Some(self.pushed(PushedStateType::CompositingLayer))
495 }
496
497 pub fn push_opacity_mask(&mut self, mask: &Rc<dyn IBrush>, bounds: Rect) -> PushedState {
502 self.core().push_opacity_mask_core(mask, bounds);
503 self.pushed(PushedStateType::OpacityMask)
504 }
505
506 pub fn push_transform(&mut self, matrix: Matrix) -> PushedState {
508 self.core().push_transform_core(matrix);
509 self.pushed(PushedStateType::Transform)
510 }
511
512 pub fn push_render_options(&mut self, render_options: RenderOptions) -> PushedState {
514 self.core().push_render_options_core(render_options);
515 self.pushed(PushedStateType::RenderOptions)
516 }
517
518 pub fn push_text_options(&mut self, text_options: TextOptions) -> PushedState {
520 self.core().push_text_options_core(text_options);
521 self.pushed(PushedStateType::TextOptions)
522 }
523
524 pub fn push_effect(&mut self, effect: &Rc<dyn IEffect>, bounds: Rect) -> PushedState {
527 self.core().push_effect_core(effect, bounds);
528 self.pushed(PushedStateType::Effect)
529 }
530}
531
532impl ITextDrawingSink for DrawingContext<'_> {
537 fn draw_glyph_run(&mut self, foreground: Option<&Rc<dyn IBrush>>, glyph_run: &Rc<GlyphRun>) {
538 DrawingContext::draw_glyph_run(self, foreground, glyph_run);
539 }
540
541 fn draw_rectangle(&mut self, brush: Option<&Rc<dyn IBrush>>, pen: Option<&Rc<dyn IPen>>, rect: Rect) {
542 DrawingContext::draw_rectangle(self, brush, pen, rect, 0.0, 0.0, &BoxShadows::default());
543 }
544
545 fn draw_line(&mut self, pen: &Rc<dyn IPen>, p1: Point, p2: Point) {
546 DrawingContext::draw_line(self, pen, p1, p2);
547 }
548
549 fn push_transform(&mut self, matrix: Matrix) {
550 let _ = DrawingContext::push_transform(self, matrix);
551 }
552
553 fn pop_transform(&mut self) {
554 let Some(states) = self.states.as_mut() else { return };
555 match states.pop() {
556 Some(PushedStateType::Transform) => self.restore(PushedStateType::Transform),
557 Some(_) => panic!("Wrong Push/Pop state order"),
558 None => {}
559 }
560 }
561}
562
563impl Drop for DrawingContext<'_> {
564 fn drop(&mut self) {
565 if !std::thread::panicking() {
568 self.dispose_in_place();
569 }
570 }
571}
572
573pub struct PushedScope<'c, 'a> {
576 context: &'c mut DrawingContext<'a>,
577 state: PushedState,
578}
579
580impl<'a> Deref for PushedScope<'_, 'a> {
581 type Target = DrawingContext<'a>;
582
583 fn deref(&self) -> &Self::Target {
584 self.context
585 }
586}
587
588impl DerefMut for PushedScope<'_, '_> {
589 fn deref_mut(&mut self) -> &mut Self::Target {
590 self.context
591 }
592}
593
594impl Drop for PushedScope<'_, '_> {
595 fn drop(&mut self) {
596 if !std::thread::panicking() {
597 self.context.pop(self.state);
598 }
599 }
600}
601
602pub(crate) fn pen_is_visible(pen: Option<&Rc<dyn IPen>>) -> bool {
604 pen.is_some_and(|pen| pen.brush().is_some() && pen.thickness() > 0.0)
605}