use super::ServerCompositionVisual;
use crate::platform::{IDrawingContextImpl, LtrbRect};
use crate::{Matrix, Rect};
use std::cell::{Cell, RefCell};
use std::rc::{Rc, Weak};
use std::sync::Arc;
pub const MAX_BACKDROP_DEPTH: usize = 2;
#[derive(Clone)]
pub(crate) struct RenderRoot {
pub(crate) visual: Rc<ServerCompositionVisual>,
pub(crate) transform: Matrix,
pub(crate) clip: LtrbRect,
pub(crate) skip_root_visual_transform: bool,
pub(crate) rendering_to_bitmap_cache: bool,
}
crate::ferro_thread_local! {
static RENDER_ROOTS: RefCell<Vec<RenderRoot>> = const { RefCell::new(Vec::new()) };
static PAINTING_VISUALS: RefCell<Vec<Rc<ServerCompositionVisual>>> = const { RefCell::new(Vec::new()) };
static BRUSH_LIGHT_IDS: RefCell<Vec<Arc<[String]>>> = const { RefCell::new(Vec::new()) };
static BACKDROP_DEPTH: Cell<usize> = const { Cell::new(0) };
static BACKDROP_REGIONS: RefCell<Vec<(Weak<ServerCompositionVisual>, LtrbRect)>> = const { RefCell::new(Vec::new()) };
}
pub(crate) fn with_render_root<R>(root: RenderRoot, f: impl FnOnce() -> R) -> R {
RENDER_ROOTS.with(|roots| roots.borrow_mut().push(root));
let result = f();
RENDER_ROOTS.with(|roots| roots.borrow_mut().pop());
result
}
pub(crate) fn with_painting_visual<R>(visual: &Rc<ServerCompositionVisual>, f: impl FnOnce() -> R) -> R {
PAINTING_VISUALS.with(|visuals| visuals.borrow_mut().push(visual.clone()));
let result = f();
PAINTING_VISUALS.with(|visuals| visuals.borrow_mut().pop());
result
}
pub fn painting_visual() -> Option<Rc<ServerCompositionVisual>> {
PAINTING_VISUALS.try_with(|visuals| visuals.borrow().last().cloned()).ok().flatten()
}
pub(crate) fn with_brush_light_ids<R>(ids: Arc<[String]>, f: impl FnOnce() -> R) -> R {
BRUSH_LIGHT_IDS.with(|stack| stack.borrow_mut().push(ids));
let result = f();
BRUSH_LIGHT_IDS.with(|stack| stack.borrow_mut().pop());
result
}
pub fn brush_light_ids() -> Arc<[String]> {
BRUSH_LIGHT_IDS
.try_with(|stack| stack.borrow().last().cloned())
.ok()
.flatten()
.unwrap_or_else(|| Arc::from(Vec::<String>::new()))
}
pub(crate) fn is_self_or_ancestor(ancestor: &ServerCompositionVisual, visual: &Rc<ServerCompositionVisual>) -> bool {
let mut current = Some(visual.clone());
let mut depth = 0;
while let Some(candidate) = current {
if std::ptr::eq(&*candidate, ancestor) {
return true;
}
depth += 1;
if depth > 1024 {
return false;
}
current = candidate.parent();
}
false
}
fn device_rect(rect: Rect, transform: Matrix) -> LtrbRect {
let corners = [
transform.transform(rect.top_left()),
transform.transform(rect.top_right()),
transform.transform(rect.bottom_left()),
transform.transform(rect.bottom_right()),
];
let (mut left, mut top, mut right, mut bottom) = (f64::MAX, f64::MAX, f64::MIN, f64::MIN);
for corner in corners {
left = left.min(corner.x);
top = top.min(corner.y);
right = right.max(corner.x);
bottom = bottom.max(corner.y);
}
LtrbRect::new(left.floor(), top.floor(), right.ceil(), bottom.ceil())
}
pub fn draw_backdrop(canvas: &mut dyn IDrawingContextImpl, rect: Rect) -> bool {
if rect.width <= 0.0 || rect.height <= 0.0 {
return false;
}
let depth = BACKDROP_DEPTH.with(Cell::get);
if depth >= MAX_BACKDROP_DEPTH {
return false;
}
let Some(visual) = painting_visual() else { return false };
let Some(root) = RENDER_ROOTS.with(|roots| roots.borrow().last().cloned()) else { return false };
if !is_self_or_ancestor(&root.visual, &visual) || Rc::ptr_eq(&root.visual, &visual) {
return false;
}
let saved = canvas.transform();
let device = device_rect(rect, saved);
BACKDROP_REGIONS.with(|regions| {
let mut regions = regions.borrow_mut();
regions.retain(|(existing, _)| existing.upgrade().is_some_and(|existing| !Rc::ptr_eq(&existing, &visual)));
regions.push((Rc::downgrade(&visual), device));
});
canvas.push_clip(rect);
canvas.set_transform(root.transform);
BACKDROP_DEPTH.with(|value| value.set(depth + 1));
let clip = root.clip.intersect_or_empty(device);
root.visual.render_up_to(
canvas,
clip,
&visual,
root.skip_root_visual_transform,
root.rendering_to_bitmap_cache,
);
BACKDROP_DEPTH.with(|value| value.set(depth));
canvas.set_transform(saved);
canvas.pop_clip();
true
}
pub(crate) fn take_backdrop_regions() -> Vec<(Weak<ServerCompositionVisual>, LtrbRect)> {
BACKDROP_REGIONS.try_with(|regions| std::mem::take(&mut *regions.borrow_mut())).unwrap_or_default()
}