use cranpose_render_common::{
graph::{ProjectiveTransform, RenderGraph, for_each_in_draw_order},
graph_scene::RenderDiagnostics,
layer_shadow::ShadowLight,
};
use super::{ContentStyle, RasterTraversalContext, populate_draws_from_graph, scene_light};
use crate::{
damage::merge_areas,
draw::{ClipBounds, TextRaster},
rewrite::Rewrite,
scene::{RasterScene, Subtree},
};
struct Repush<'a> {
scene: &'a mut RasterScene,
reach: &'a mut Vec<Option<ClipBounds>>,
held: UpdatedReach,
rewrite: &'a mut Option<Box<Rewrite>>,
window: ClipBounds,
areas: &'a mut Vec<ClipBounds>,
diagnostics: &'a RenderDiagnostics,
light: ShadowLight,
scale: f32,
text: &'a TextRaster,
}
enum Stop {
OtherLayer,
Rebuild,
}
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub(crate) enum UpdatedReach {
Held,
#[default]
Lost,
}
pub(crate) struct InPlaceUpdate<'a> {
pub scene: &'a mut RasterScene,
pub reach: &'a mut Vec<Option<ClipBounds>>,
pub rewrite: &'a mut Option<Box<Rewrite>>,
}
impl InPlaceUpdate<'_> {
pub(crate) fn run(
self,
graph: &RenderGraph,
diagnostics: &RenderDiagnostics,
scale: f32,
text: &TextRaster,
window: ClipBounds,
areas: &mut Vec<ClipBounds>,
) -> Option<UpdatedReach> {
let InPlaceUpdate {
scene,
reach,
rewrite,
} = self;
areas.clear();
let light = scene_light(graph, scale);
if scene.start != Some((scale, light)) {
return None;
}
let held = if reach.len() == scene.order.len() {
UpdatedReach::Held
} else {
UpdatedReach::Lost
};
let mut repush = Repush {
scene,
reach,
held,
rewrite,
window,
areas,
diagnostics,
light,
scale,
text,
};
let walked = repush.visit(Subtree::Layer(&graph.root), 0, None);
let held = repush.held;
merge_areas(areas);
walked.ok()?;
Some(held)
}
}
impl Repush<'_> {
fn visit(
&mut self,
subtree: Subtree<'_>,
record: usize,
parent: Option<usize>,
) -> Result<(), Stop> {
let old = self.scene.subtrees.get(record).ok_or(Stop::OtherLayer)?;
if old.identity != subtree.identity() {
return Err(Stop::OtherLayer);
}
let placed_as_before = old.transform == subtree.transform_to_parent();
if old.change == subtree.change() && placed_as_before {
return Ok(());
}
let Subtree::Layer(layer) = subtree else {
return self.push_again(subtree, record, parent);
};
if old.own_change != layer.own_change || !placed_as_before || old.end == record + 1 {
return self.push_again(subtree, record, parent);
}
let Some(passed) = old.passed_down else {
return self.push_again(subtree, record, parent);
};
let mut next = record + 1;
let mut walked = Ok(());
for_each_in_draw_order(&layer.children, |child| {
if walked.is_ok()
&& let Some(child) = Subtree::of_child(child)
{
walked = self.visit(child, next, Some(passed));
next = self
.scene
.subtrees
.get(next)
.map_or(usize::MAX, |at| at.end);
}
});
match walked {
Err(Stop::Rebuild) => Err(Stop::Rebuild),
Ok(()) if next == self.scene.subtrees[record].end => Ok(()),
Ok(()) | Err(Stop::OtherLayer) => self.push_again(subtree, record, parent),
}
}
fn push_again(
&mut self,
subtree: Subtree<'_>,
record: usize,
parent: Option<usize>,
) -> Result<(), Stop> {
let passed = parent.and_then(|at| self.scene.passed_down.get(at));
let transform = passed.map_or_else(
|| ProjectiveTransform::uniform_scale(self.scale),
|passed| passed.transform,
);
let style = passed.map_or(ContentStyle::PLAIN, |passed| ContentStyle {
alpha: passed.alpha,
color_filter: passed.color_filter.clone(),
});
let context = RasterTraversalContext {
parent_transform: &transform,
parent_content_style: &style,
parent_visual_clip: passed.and_then(|passed| passed.clip),
inherited_translated_snap_anchor: passed.and_then(|passed| passed.snap_anchor),
inherited_translated_content_context: passed.is_some_and(|passed| passed.translated),
diagnostics: self.diagnostics,
light: self.light,
same_as_before: false,
text: self.text,
surface_root: false,
in_space: false,
};
let mut rewrite = self.rewrite.take().unwrap_or_default();
if !rewrite.start(self.scene, record, self.window, self.reach, self.held) {
*self.rewrite = Some(rewrite);
return Err(Stop::Rebuild);
}
self.scene.rewrite = Some(rewrite);
populate_draws_from_graph(subtree, self.scene, &context, &mut None);
let mut rewrite = self
.scene
.rewrite
.take()
.expect("the rewrite started above");
rewrite.finish(self.scene, record);
let (reach, held, areas) = rewrite.results();
(*self.reach, self.held) = (reach, held);
self.areas.append(areas);
*self.rewrite = Some(rewrite);
Ok(())
}
}