use crate::pipeline::{
emitted_scene_bounds, push_draw_primitive, push_layer_shadow, push_text_style_draws,
scene_emission_counts, SceneEmissionCounts,
};
use crate::scene::{
BackdropLayer, CompositorScene, DrawShape, EffectLayer, ImageDraw, ShadowDraw, SnapAnchor,
TextDraw,
};
use crate::surface_plan::{
layer_cache_key, layer_contains_descendant_backdrop, layer_needs_text_leaf_snap,
layer_surface_requirements_cached, LayerSurfaceRequirements, TranslationRenderContext,
};
use crate::surface_requirements::{SurfaceRequirement, SurfaceRequirementSet};
use cranpose_render_common::geometry::{expand_blurred_rect, union_rect};
use cranpose_render_common::graph::{
quad_bounds, LayerNode, PrimitiveEntry, PrimitiveNode, PrimitivePhase, RenderNode,
};
use cranpose_render_common::layer_composition::{
effective_layer_isolation, layer_for_content, local_content_layer,
};
use cranpose_render_common::primitive_emit::{
resolve_clip, resolve_primitive_clip, PrimitiveClipSpace,
};
use cranpose_ui_graphics::{GraphicsLayer, Point, Rect};
use std::collections::HashMap;
const NORMALIZED_SCENE_AFFINE_TOLERANCE: f32 = 1e-4;
pub(crate) struct ChildLayerComposite<'a> {
pub(crate) z_index: usize,
pub(crate) layer: &'a LayerNode,
pub(crate) logical_rect: Rect,
pub(crate) dest_quad: [[f32; 2]; 4],
pub(crate) backdrop_rect: Rect,
pub(crate) visual_clip: Option<Rect>,
pub(crate) shadow_draws: Vec<ShadowDraw>,
pub(crate) needs_nested_underlay: bool,
}
#[derive(Clone)]
pub(crate) struct ResolvedChildSurfaceComposite {
pub(crate) logical_rect: Rect,
pub(crate) dest_quad: [[f32; 2]; 4],
pub(crate) backdrop_rect: Rect,
pub(crate) shadow_draws: Vec<ShadowDraw>,
}
pub(crate) struct CollectedLayer<'a> {
pub(crate) scene: CompositorScene,
pub(crate) child_layers: Vec<ChildLayerComposite<'a>>,
}
pub(crate) fn visible_draw_rect(rect: Rect, clip: Option<Rect>) -> Option<Rect> {
match clip {
Some(clip) => rect.intersect(clip),
None => Some(rect),
}
}
fn scene_bounds_with_clip(scene: &CompositorScene, apply_clip: bool) -> Option<Rect> {
let mut bounds = None;
for shape in &scene.shapes {
let rect = if apply_clip {
visible_draw_rect(shape.rect, shape.clip)
} else {
Some(shape.rect)
};
if let Some(visible) = rect {
bounds = union_rect(bounds, visible);
}
}
for image in &scene.images {
let rect = if apply_clip {
visible_draw_rect(image.rect, image.clip)
} else {
Some(image.rect)
};
if let Some(visible) = rect {
bounds = union_rect(bounds, visible);
}
}
for text in &scene.texts {
let rect = if apply_clip {
visible_draw_rect(text.rect, text.clip)
} else {
Some(text.rect)
};
if let Some(visible) = rect {
bounds = union_rect(bounds, visible);
}
}
if let Some(shadow_bounds) = shadow_draws_bounds_with_clip(&scene.shadow_draws, apply_clip) {
bounds = union_rect(bounds, shadow_bounds);
}
for layer in &scene.effect_layers {
let rect = if apply_clip {
visible_draw_rect(layer.rect, layer.clip)
} else {
Some(layer.rect)
};
if let Some(visible) = rect {
bounds = union_rect(bounds, visible);
}
}
for layer in &scene.backdrop_layers {
let rect = if apply_clip {
visible_draw_rect(layer.rect, layer.clip)
} else {
Some(layer.rect)
};
if let Some(visible) = rect {
bounds = union_rect(bounds, visible);
}
}
bounds
}
pub(crate) fn scene_bounds(scene: &CompositorScene) -> Option<Rect> {
scene_bounds_with_clip(scene, true)
}
fn scene_capture_bounds(scene: &CompositorScene) -> Option<Rect> {
scene_bounds_with_clip(scene, false)
}
pub(crate) fn scene_has_layer_events(scene: &CompositorScene) -> bool {
!scene.effect_layers.is_empty() || !scene.backdrop_layers.is_empty()
}
fn shadow_draws_bounds_with_clip(shadow_draws: &[ShadowDraw], apply_clip: bool) -> Option<Rect> {
let mut bounds = None;
for shadow in shadow_draws {
let mut shadow_bounds = None;
for (shape, _) in &shadow.shapes {
shadow_bounds = union_rect(shadow_bounds, shape.rect);
}
for text in &shadow.texts {
shadow_bounds = union_rect(shadow_bounds, text.rect);
}
if let Some(shadow_bounds) = shadow_bounds {
let clip = apply_clip.then_some(shadow.clip).flatten();
if let Some(expanded) = expand_blurred_rect(shadow_bounds, shadow.blur_radius, clip) {
bounds = union_rect(bounds, expanded);
}
}
}
bounds
}
fn shadow_draws_bounds(shadow_draws: &[ShadowDraw]) -> Option<Rect> {
shadow_draws_bounds_with_clip(shadow_draws, true)
}
pub(crate) fn collected_layer_bounds(
scene: &CompositorScene,
child_layers: &[ChildLayerComposite<'_>],
apply_clip: bool,
) -> Option<Rect> {
let mut bounds = if apply_clip {
scene_bounds(scene)
} else {
scene_capture_bounds(scene)
};
for child in child_layers {
let child_bounds = quad_bounds(child.dest_quad);
let rect = if apply_clip {
visible_draw_rect(child_bounds, child.visual_clip)
} else {
Some(child_bounds)
};
if let Some(visible) = rect {
bounds = union_rect(bounds, visible);
}
let shadow_bounds = if apply_clip {
shadow_draws_bounds(&child.shadow_draws)
} else {
shadow_draws_bounds_with_clip(&child.shadow_draws, false)
};
if let Some(shadow_bounds) = shadow_bounds {
bounds = union_rect(bounds, shadow_bounds);
}
}
bounds
}
fn hidden_content_precedes_visible_bounds(visible_bounds: Rect, full_bounds: Rect) -> bool {
const TOLERANCE: f32 = 1e-4;
full_bounds.x < visible_bounds.x - TOLERANCE || full_bounds.y < visible_bounds.y - TOLERANCE
}
pub(crate) fn motion_stable_capture_bounds(
layer: &LayerNode,
scene: &CompositorScene,
child_layers: &[ChildLayerComposite<'_>],
requirements: SurfaceRequirementSet,
) -> Option<Rect> {
let visible_bounds = collected_layer_bounds(scene, child_layers, true);
if !requirements.contains(SurfaceRequirement::MotionStableCapture)
|| layer.effect().is_some()
|| layer.backdrop().is_some()
{
return visible_bounds;
}
let full_bounds = collected_layer_bounds(scene, child_layers, false);
match (visible_bounds, full_bounds) {
(Some(visible_bounds), Some(full_bounds))
if hidden_content_precedes_visible_bounds(visible_bounds, full_bounds) =>
{
Some(full_bounds)
}
_ => visible_bounds.or(full_bounds),
}
}
fn graphics_layer_supports_rigid_snap(layer: &GraphicsLayer) -> bool {
(layer.scale - 1.0).abs() <= NORMALIZED_SCENE_AFFINE_TOLERANCE
&& (layer.scale_x - 1.0).abs() <= NORMALIZED_SCENE_AFFINE_TOLERANCE
&& (layer.scale_y - 1.0).abs() <= NORMALIZED_SCENE_AFFINE_TOLERANCE
&& layer.rotation_x.abs() <= NORMALIZED_SCENE_AFFINE_TOLERANCE
&& layer.rotation_y.abs() <= NORMALIZED_SCENE_AFFINE_TOLERANCE
&& layer.rotation_z.abs() <= NORMALIZED_SCENE_AFFINE_TOLERANCE
}
fn rigid_snap_anchor(
layer_bounds: Rect,
layer: &GraphicsLayer,
motion_context_animated: bool,
) -> Option<SnapAnchor> {
if motion_context_animated || !graphics_layer_supports_rigid_snap(layer) {
return None;
}
let mapped = cranpose_render_common::layer_transform::apply_layer_affine_to_rect(
layer_bounds,
layer_bounds,
layer,
);
Some(SnapAnchor::rigid(Point::new(mapped.x, mapped.y)))
}
#[derive(Clone, Copy)]
struct SceneCounts {
shapes: usize,
images: usize,
texts: usize,
shadow_draws: usize,
effect_layers: usize,
}
fn scene_counts(scene: &CompositorScene) -> SceneCounts {
SceneCounts {
shapes: scene.shapes.len(),
images: scene.images.len(),
texts: scene.texts.len(),
shadow_draws: scene.shadow_draws.len(),
effect_layers: scene.effect_layers.len(),
}
}
fn assign_snap_anchor_since(
scene: &mut CompositorScene,
counts: SceneCounts,
snap_anchor: Option<SnapAnchor>,
) {
let Some(snap_anchor) = snap_anchor else {
return;
};
for shape in &mut scene.shapes[counts.shapes..] {
shape.snap_anchor = Some(snap_anchor);
}
for image in &mut scene.images[counts.images..] {
image.snap_anchor = Some(snap_anchor);
}
for text in &mut scene.texts[counts.texts..] {
text.snap_anchor = Some(snap_anchor);
}
for shadow in &mut scene.shadow_draws[counts.shadow_draws..] {
for (shape, _) in &mut shadow.shapes {
shape.snap_anchor = Some(snap_anchor);
}
for text in &mut shadow.texts {
text.snap_anchor = Some(snap_anchor);
}
}
}
fn mark_translated_text_since(
scene: &mut CompositorScene,
counts: SceneCounts,
translated_content_context: bool,
) {
if !translated_content_context {
return;
}
for text in &mut scene.texts[counts.texts..] {
text.translated_content_context = true;
}
}
fn mark_motion_stable_effect_layers_since(
scene: &mut CompositorScene,
counts: SceneCounts,
translated_content_context: bool,
) {
if !translated_content_context {
return;
}
for layer in &mut scene.effect_layers[counts.effect_layers..] {
layer
.requirements
.insert(SurfaceRequirement::MotionStableCapture);
}
}
#[derive(Clone, Copy)]
struct TranslatedLocalPictureState {
counts: SceneEmissionCounts,
z_start: usize,
}
fn flush_translated_local_picture(
scene: &mut CompositorScene,
state: &mut Option<TranslatedLocalPictureState>,
clip: Option<Rect>,
) {
let Some(current) = *state else {
return;
};
let z_end = scene.next_z;
if z_end > current.z_start {
if let Some(surface_rect) = emitted_scene_bounds(scene, current.counts) {
scene.push_effect_layer_with_requirements(
surface_rect,
clip,
None,
cranpose_ui_graphics::BlendMode::SrcOver,
1.0,
current.z_start,
z_end,
SurfaceRequirementSet::default().with(SurfaceRequirement::MotionStableCapture),
);
}
}
*state = Some(TranslatedLocalPictureState {
counts: scene_emission_counts(scene),
z_start: scene.next_z,
});
}
pub(crate) fn resolved_child_surface_composite(
child: &ChildLayerComposite<'_>,
) -> ResolvedChildSurfaceComposite {
ResolvedChildSurfaceComposite {
logical_rect: child.logical_rect,
dest_quad: child.dest_quad,
backdrop_rect: child.backdrop_rect,
shadow_draws: child.shadow_draws.clone(),
}
}
struct LocalPrimitiveContext<'a> {
layer_bounds: Rect,
local_layer: &'a GraphicsLayer,
visual_clip: Option<Rect>,
motion_context_animated: bool,
content_offset_translation: bool,
translated_text_motion: bool,
draw_snap_anchor: Option<SnapAnchor>,
text_snap_anchor: Option<SnapAnchor>,
}
fn push_local_primitive(
local_scene: &mut CompositorScene,
primitive: &PrimitiveEntry,
context: &LocalPrimitiveContext<'_>,
) {
match &primitive.node {
PrimitiveNode::Draw(draw) => {
let counts_before = scene_counts(local_scene);
let clip = resolve_primitive_clip(
draw.clip,
context.layer_bounds,
context.local_layer,
context.visual_clip,
PrimitiveClipSpace::Local,
);
if draw.clip.is_some() && clip.is_none() {
return;
}
push_draw_primitive(
draw.primitive.clone(),
context.layer_bounds,
context.local_layer,
clip,
local_scene,
None,
context.motion_context_animated || context.content_offset_translation,
);
assign_snap_anchor_since(local_scene, counts_before, context.draw_snap_anchor);
}
PrimitiveNode::Text(text) => {
let counts_before = scene_counts(local_scene);
let text_clip = resolve_primitive_clip(
text.clip,
context.layer_bounds,
context.local_layer,
context.visual_clip,
PrimitiveClipSpace::Local,
);
if text.clip.is_some() && text_clip.is_none() {
return;
}
let text_rect = text
.rect
.translate(context.layer_bounds.x, context.layer_bounds.y);
push_text_style_draws(
local_scene,
text.node_id,
context.layer_bounds,
text_rect,
context.local_layer,
&text.text,
&text.text_style,
text.font_size,
text.layout_options,
text_clip,
);
mark_motion_stable_effect_layers_since(
local_scene,
counts_before,
context.content_offset_translation,
);
mark_translated_text_since(local_scene, counts_before, context.translated_text_motion);
assign_snap_anchor_since(local_scene, counts_before, context.text_snap_anchor);
}
}
}
pub(crate) fn translate_quad(quad: [[f32; 2]; 4], delta: Point) -> [[f32; 2]; 4] {
quad.map(|[x, y]| [x + delta.x, y + delta.y])
}
#[allow(clippy::too_many_arguments)]
fn collect_layer_contents_into<'a>(
layer: &'a LayerNode,
inherited_clip: Option<Rect>,
layer_offset: Point,
inherited_translated_snap_anchor: Option<SnapAnchor>,
translation_context: TranslationRenderContext,
local_scene: &mut CompositorScene,
child_layers: &mut Vec<ChildLayerComposite<'a>>,
layer_surface_rect_cache: &mut HashMap<usize, Rect>,
layer_surface_requirements_cache: &mut HashMap<usize, LayerSurfaceRequirements>,
) {
let isolation = effective_layer_isolation(&layer.graphics_layer);
let content_layer = layer_for_content(&layer.graphics_layer, isolation.as_ref());
let local_layer = local_content_layer(&content_layer);
let layer_bounds = layer.local_bounds.translate(layer_offset.x, layer_offset.y);
let layer_clip = layer
.clip_rect()
.map(|clip| clip.translate(layer_offset.x, layer_offset.y));
let visual_clip = resolve_clip(inherited_clip, layer_clip);
if layer_clip.is_some() && inherited_clip.is_some() && visual_clip.is_none() {
return;
}
let effective_translated_content_context =
translation_context.inherited_content_translation || layer.translated_content_context;
let translated_snap_anchor = if effective_translated_content_context {
None
} else {
inherited_translated_snap_anchor
};
let layer_snap_anchor = if effective_translated_content_context {
None
} else {
translated_snap_anchor.or_else(|| {
if layer_needs_text_leaf_snap(layer) {
rigid_snap_anchor(layer_bounds, &local_layer, layer.motion_context_animated)
} else {
None
}
})
};
let translated_local_picture = layer.translated_content_context
&& !translation_context.inherited_content_translation
&& !translation_context.surface_capture_active;
let translated_text_motion =
effective_translated_content_context && !translation_context.surface_capture_active;
let mut translated_local_picture_state =
translated_local_picture.then(|| TranslatedLocalPictureState {
counts: scene_emission_counts(local_scene),
z_start: local_scene.next_z,
});
let local_primitive_context = LocalPrimitiveContext {
layer_bounds,
local_layer: &local_layer,
visual_clip,
motion_context_animated: layer.motion_context_animated,
content_offset_translation: effective_translated_content_context,
translated_text_motion,
draw_snap_anchor: layer_snap_anchor,
text_snap_anchor: layer_snap_anchor,
};
let child_translation_context = TranslationRenderContext {
inherited_content_translation: effective_translated_content_context,
surface_capture_active: translation_context.surface_capture_active,
};
let mut deferred_primitives = Vec::new();
for child in &layer.children {
match child {
RenderNode::Primitive(primitive) => match primitive.phase {
PrimitivePhase::BeforeChildren => {
push_local_primitive(local_scene, primitive, &local_primitive_context);
}
PrimitivePhase::AfterChildren => deferred_primitives.push(primitive),
},
RenderNode::Layer(child_layer) => {
let child_requirements = layer_surface_requirements_cached(
child_layer.as_ref(),
layer_surface_requirements_cache,
);
if !child_requirements.has_isolating_requirement() {
let translation = child_requirements
.direct_translation
.expect("direct child layers must keep a pure translation transform");
let child_offset = Point::new(
layer_offset.x + translation.x,
layer_offset.y + translation.y,
);
let child_bounds = child_layer
.local_bounds
.translate(child_offset.x, child_offset.y);
let child_shadow_clip = resolve_clip(
visual_clip,
child_layer
.shadow_clip
.map(|clip| clip.translate(child_offset.x, child_offset.y)),
);
push_layer_shadow(
local_scene,
&child_layer.graphics_layer,
child_bounds,
child_bounds,
child_shadow_clip,
);
collect_layer_contents_into(
child_layer.as_ref(),
visual_clip,
child_offset,
translated_snap_anchor,
child_translation_context,
local_scene,
child_layers,
layer_surface_rect_cache,
layer_surface_requirements_cache,
);
continue;
}
flush_translated_local_picture(
local_scene,
&mut translated_local_picture_state,
visual_clip,
);
let mut shadow_scene = CompositorScene::new();
let child_logical_rect = estimate_layer_surface_rect_cached(
child_layer.as_ref(),
layer_surface_rect_cache,
layer_surface_requirements_cache,
);
let child_bounds = quad_bounds(
child_layer
.transform_to_parent
.map_rect(child_layer.local_bounds),
);
let child_bounds = child_bounds.translate(layer_offset.x, layer_offset.y);
let child_shadow_clip = resolve_clip(
visual_clip,
child_layer.shadow_clip.map(|clip| {
quad_bounds(child_layer.transform_to_parent.map_rect(clip))
.translate(layer_offset.x, layer_offset.y)
}),
);
push_layer_shadow(
&mut shadow_scene,
&child_layer.graphics_layer,
child_layer.local_bounds,
child_bounds,
child_shadow_clip,
);
child_layers.push(ChildLayerComposite {
z_index: local_scene.next_z,
layer: child_layer.as_ref(),
logical_rect: child_logical_rect,
dest_quad: translate_quad(
child_layer.transform_to_parent.map_rect(child_logical_rect),
layer_offset,
),
backdrop_rect: quad_bounds(translate_quad(
child_layer
.transform_to_parent
.map_rect(child_layer.local_bounds),
layer_offset,
)),
visual_clip,
shadow_draws: shadow_scene.shadow_draws,
needs_nested_underlay: layer_contains_descendant_backdrop(child_layer.as_ref()),
});
local_scene.next_z += 1;
if translated_local_picture_state.is_some() {
translated_local_picture_state = Some(TranslatedLocalPictureState {
counts: scene_emission_counts(local_scene),
z_start: local_scene.next_z,
});
}
}
}
}
for primitive in deferred_primitives {
push_local_primitive(local_scene, primitive, &local_primitive_context);
}
flush_translated_local_picture(
local_scene,
&mut translated_local_picture_state,
visual_clip,
);
}
pub(crate) fn collect_layer_contents<'a>(
layer: &'a LayerNode,
inherited_clip: Option<Rect>,
inherited_translated_snap_anchor: Option<SnapAnchor>,
layer_surface_rect_cache: &mut HashMap<usize, Rect>,
layer_surface_requirements_cache: &mut HashMap<usize, LayerSurfaceRequirements>,
) -> CollectedLayer<'a> {
collect_layer_contents_with_translation_context(
layer,
inherited_clip,
inherited_translated_snap_anchor,
TranslationRenderContext::default(),
layer_surface_rect_cache,
layer_surface_requirements_cache,
)
}
pub(crate) fn collect_layer_contents_with_translation_context<'a>(
layer: &'a LayerNode,
inherited_clip: Option<Rect>,
inherited_translated_snap_anchor: Option<SnapAnchor>,
translation_context: TranslationRenderContext,
layer_surface_rect_cache: &mut HashMap<usize, Rect>,
layer_surface_requirements_cache: &mut HashMap<usize, LayerSurfaceRequirements>,
) -> CollectedLayer<'a> {
let mut local_scene = CompositorScene::new();
let mut child_layers = Vec::new();
collect_layer_contents_with_translation_context_into(
layer,
inherited_clip,
inherited_translated_snap_anchor,
translation_context,
&mut local_scene,
&mut child_layers,
layer_surface_rect_cache,
layer_surface_requirements_cache,
);
CollectedLayer {
scene: local_scene,
child_layers,
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn collect_layer_contents_with_translation_context_into<'a>(
layer: &'a LayerNode,
inherited_clip: Option<Rect>,
inherited_translated_snap_anchor: Option<SnapAnchor>,
translation_context: TranslationRenderContext,
local_scene: &mut CompositorScene,
child_layers: &mut Vec<ChildLayerComposite<'a>>,
layer_surface_rect_cache: &mut HashMap<usize, Rect>,
layer_surface_requirements_cache: &mut HashMap<usize, LayerSurfaceRequirements>,
) {
local_scene.clear();
child_layers.clear();
collect_layer_contents_into(
layer,
inherited_clip,
Point::default(),
inherited_translated_snap_anchor,
translation_context,
local_scene,
child_layers,
layer_surface_rect_cache,
layer_surface_requirements_cache,
);
}
#[cfg(test)]
pub(crate) fn estimate_layer_surface_rect(layer: &LayerNode) -> Rect {
let mut layer_surface_rect_cache = HashMap::new();
let mut layer_surface_requirements_cache = HashMap::new();
estimate_layer_surface_rect_cached(
layer,
&mut layer_surface_rect_cache,
&mut layer_surface_requirements_cache,
)
}
pub(crate) fn estimate_layer_surface_rect_cached(
layer: &LayerNode,
layer_surface_rect_cache: &mut HashMap<usize, Rect>,
layer_surface_requirements_cache: &mut HashMap<usize, LayerSurfaceRequirements>,
) -> Rect {
let cache_key = layer_cache_key(layer);
if let Some(cached_rect) = layer_surface_rect_cache.get(&cache_key) {
return *cached_rect;
}
let collected = collect_layer_contents(
layer,
None,
None,
layer_surface_rect_cache,
layer_surface_requirements_cache,
);
let surface_requirements =
layer_surface_requirements_cached(layer, layer_surface_requirements_cache);
let bounds = motion_stable_capture_bounds(
layer,
&collected.scene,
&collected.child_layers,
surface_requirements.surface_requirements,
);
let rect = resolved_layer_surface_rect(layer, bounds);
layer_surface_rect_cache.insert(cache_key, rect);
rect
}
pub(crate) fn resolved_layer_surface_rect(layer: &LayerNode, bounds: Option<Rect>) -> Rect {
let rect = bounds.unwrap_or(layer.local_bounds);
if layer.effect().is_some() || layer.backdrop().is_some() {
union_rect(Some(rect), layer.local_bounds).unwrap_or(rect)
} else {
rect
}
}
pub(crate) trait TranslateBy {
fn translate_by(&mut self, delta: Point);
}
impl TranslateBy for Rect {
fn translate_by(&mut self, delta: Point) {
self.x += delta.x;
self.y += delta.y;
}
}
impl TranslateBy for DrawShape {
fn translate_by(&mut self, delta: Point) {
self.rect.translate_by(delta);
self.local_rect.translate_by(delta);
for point in &mut self.quad {
point[0] += delta.x;
point[1] += delta.y;
}
if let Some(clip) = self.clip.as_mut() {
clip.translate_by(delta);
}
}
}
impl TranslateBy for ImageDraw {
fn translate_by(&mut self, delta: Point) {
self.rect.translate_by(delta);
self.local_rect.translate_by(delta);
for point in &mut self.quad {
point[0] += delta.x;
point[1] += delta.y;
}
if let Some(clip) = self.clip.as_mut() {
clip.translate_by(delta);
}
}
}
impl TranslateBy for TextDraw {
fn translate_by(&mut self, delta: Point) {
self.rect.translate_by(delta);
if let Some(clip) = self.clip.as_mut() {
clip.translate_by(delta);
}
}
}
impl TranslateBy for ShadowDraw {
fn translate_by(&mut self, delta: Point) {
for (shape, _) in &mut self.shapes {
shape.translate_by(delta);
}
for text in &mut self.texts {
text.translate_by(delta);
}
if let Some(clip) = self.clip.as_mut() {
clip.translate_by(delta);
}
}
}
impl TranslateBy for EffectLayer {
fn translate_by(&mut self, delta: Point) {
self.rect.translate_by(delta);
if let Some(clip) = self.clip.as_mut() {
clip.translate_by(delta);
}
}
}
impl TranslateBy for BackdropLayer {
fn translate_by(&mut self, delta: Point) {
self.rect.translate_by(delta);
if let Some(clip) = self.clip.as_mut() {
clip.translate_by(delta);
}
}
}
impl<T: TranslateBy> TranslateBy for Vec<T> {
fn translate_by(&mut self, delta: Point) {
for item in self {
item.translate_by(delta);
}
}
}
impl TranslateBy for CompositorScene {
fn translate_by(&mut self, delta: Point) {
self.shapes.translate_by(delta);
self.images.translate_by(delta);
self.texts.translate_by(delta);
self.shadow_draws.translate_by(delta);
self.effect_layers.translate_by(delta);
self.backdrop_layers.translate_by(delta);
}
}
pub(crate) struct SceneWindowSource<'a> {
pub(crate) shapes: &'a [DrawShape],
pub(crate) images: &'a [ImageDraw],
pub(crate) texts: &'a [TextDraw],
pub(crate) shadow_draws: &'a [ShadowDraw],
pub(crate) effect_layers: &'a [EffectLayer],
pub(crate) backdrop_layers: &'a [BackdropLayer],
}
pub(crate) fn effect_layer_in_range(layer: &EffectLayer, z_start: usize, z_end: usize) -> bool {
layer.z_start >= z_start && layer.z_start < z_end && layer.z_end <= z_end
}
pub(crate) fn build_scene_window(
source: SceneWindowSource<'_>,
z_start: usize,
z_end: usize,
window_rect: Rect,
) -> CompositorScene {
let mut scene = CompositorScene::new();
scene.shapes = source
.shapes
.iter()
.filter(|shape| shape.z_index >= z_start && shape.z_index < z_end)
.cloned()
.collect();
scene.images = source
.images
.iter()
.filter(|image| image.z_index >= z_start && image.z_index < z_end)
.cloned()
.collect();
scene.texts = source
.texts
.iter()
.filter(|text| text.z_index >= z_start && text.z_index < z_end)
.cloned()
.collect();
scene.shadow_draws = source
.shadow_draws
.iter()
.filter(|shadow| shadow.z_index >= z_start && shadow.z_index < z_end)
.cloned()
.collect();
scene.effect_layers = source
.effect_layers
.iter()
.filter(|layer| effect_layer_in_range(layer, z_start, z_end))
.cloned()
.collect();
scene.backdrop_layers = source
.backdrop_layers
.iter()
.filter(|layer| layer.z_index >= z_start && layer.z_index < z_end)
.cloned()
.collect();
scene.translate_by(Point {
x: -window_rect.x,
y: -window_rect.y,
});
scene
}
pub(crate) fn filtered_effect_layer_index(
effect_layers: &[EffectLayer],
effect_layer_index: usize,
z_start: usize,
z_end: usize,
) -> Option<usize> {
let mut filtered_index = 0usize;
for (index, layer) in effect_layers.iter().enumerate() {
if !effect_layer_in_range(layer, z_start, z_end) {
continue;
}
if index == effect_layer_index {
return Some(filtered_index);
}
filtered_index += 1;
}
None
}