use crate::display_list::{BackdropKey, DisplayList, PaintCommand, SurfacePartitionId};
use crate::effects::{
BlendMode, ColorFilter, DropShadowEffect, GaussianBlur, blur_bounds, drop_shadow_bounds,
};
use crate::geometry::{RRect, union_rect};
use crate::gradients::Brush;
use crate::paint::FillRule;
use crate::paths::{Path, ellipse_as_path, fallback_tolerance, rect_as_path, rrect_as_path};
use incular_core::finite_or_zero;
use incular_core::{Arena, ArenaId, Offset, Rect, Size, Transform};
use std::{
any::{Any, TypeId},
cell::RefCell,
collections::HashMap,
rc::Rc,
sync::Arc,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LayerId(ArenaId);
impl LayerId {
#[must_use]
pub const fn raw(self) -> u64 {
((self.0.generation() as u64) << 32) | self.0.index() as u64
}
}
#[must_use]
pub fn normalize_opacity(alpha: f32) -> f32 {
if alpha.is_finite() {
alpha.clamp(0., 1.)
} else {
0.
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CompositorDiagnostics {
pub layers: u64,
pub picture_layers_repainted: u64,
pub transform_updates: u64,
pub clip_updates: u64,
pub layers_culled: u64,
pub opacity_updates: u64,
pub leader_publish_passes: u64,
pub leaders_published: u64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FlattenedPicture {
pub layer: LayerId,
pub local_bounds: Rect,
pub world_bounds: Rect,
pub active_clip: Option<Rect>,
}
#[derive(Clone)]
pub struct Annotation {
value: Rc<dyn Any>,
type_id: TypeId,
}
impl Annotation {
#[must_use]
pub fn new<T: Any>(value: T) -> Self {
Self {
value: Rc::new(value),
type_id: TypeId::of::<T>(),
}
}
#[must_use]
pub const fn value_type_id(&self) -> TypeId {
self.type_id
}
#[must_use]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.value.downcast_ref::<T>()
}
#[must_use]
pub fn cloned<T: Any + Clone>(&self) -> Option<T> {
self.downcast_ref::<T>().cloned()
}
}
impl std::fmt::Debug for Annotation {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Annotation")
.field("type_id", &self.type_id)
.finish_non_exhaustive()
}
}
impl PartialEq for Annotation {
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id && Rc::ptr_eq(&self.value, &other.value)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct LayerAnchor {
pub x: f32,
pub y: f32,
}
impl LayerAnchor {
pub const TOP_LEFT: Self = Self { x: -1., y: -1. };
#[must_use]
pub fn new(x: f32, y: f32) -> Self {
Self {
x: finite_or_zero(x),
y: finite_or_zero(y),
}
}
#[must_use]
pub const fn along_size(self, size: Size) -> Offset {
Offset::new(
size.width * (self.x + 1.) * 0.5,
size.height * (self.y + 1.) * 0.5,
)
}
}
fn clip_world_bounds(kind: &LayerKind, world_transform: Transform) -> Option<Rect> {
match kind {
LayerKind::ClipRect { rect, .. } => Some(world_transform.transform_rect_bbox(*rect)),
LayerKind::ClipRRect { rrect, .. } => Some(world_transform.transform_rect_bbox(rrect.rect)),
LayerKind::ClipOval { rect, .. } => Some(world_transform.transform_rect_bbox(*rect)),
LayerKind::ClipPath { path, .. } => path
.bounds()
.map(|bounds| world_transform.transform_rect_bbox(bounds)),
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum ClipSpace {
Translation,
Scale { x: f32, y: f32 },
General,
}
fn clip_space(world: Transform) -> ClipSpace {
let [a, b, c, d, _, _] = world.to_kurbo().as_coeffs();
if b == 0. && c == 0. {
if a == 1. && d == 1. {
ClipSpace::Translation
} else {
ClipSpace::Scale {
x: a as f32,
y: d as f32,
}
}
} else {
ClipSpace::General
}
}
#[must_use]
pub fn resolve_follower_target(
leader_transform: Transform,
leader_size: Size,
target_anchor: LayerAnchor,
offset: Offset,
) -> Offset {
let target_point = leader_transform.transform_point(target_anchor.along_size(leader_size));
let leader_origin = leader_transform.transform_point(Offset::ZERO);
target_point + (leader_transform.transform_point(offset) - leader_origin)
}
#[derive(Clone, Debug)]
struct ResolvedClipPath {
world: Transform,
path: Arc<Path>,
}
#[derive(Clone, Debug)]
struct LeaderData {
owner: PublisherIdentity,
transform: Transform,
size: Size,
generation: u64,
}
struct LeaderRecord {
id: LayerId,
link: LayerLink,
size: Size,
publisher: PublisherIdentity,
generation: u64,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum PublishState {
InProgress,
Published,
Unresolvable,
}
#[derive(Clone, Debug)]
pub struct PublisherIdentity(Rc<()>);
impl PublisherIdentity {
pub(crate) fn new() -> Self {
Self(Rc::new(()))
}
#[must_use]
pub fn same_owner(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
#[derive(Clone)]
pub struct LayerLink(Rc<RefCell<Option<LeaderData>>>);
impl Default for LayerLink {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for LayerLink {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("LayerLink")
.field("linked", &self.is_linked())
.finish()
}
}
impl PartialEq for LayerLink {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl LayerLink {
#[must_use]
pub fn new() -> Self {
Self(Rc::new(RefCell::new(None)))
}
#[must_use]
pub fn is_linked(&self) -> bool {
self.0.borrow().is_some()
}
#[must_use]
pub fn leader_size(&self) -> Option<Size> {
self.0.borrow().as_ref().map(|leader| leader.size)
}
#[must_use]
pub fn leader_transform(&self) -> Option<Transform> {
self.0.borrow().as_ref().map(|leader| leader.transform)
}
#[must_use]
pub fn leader_generation(&self) -> u64 {
self.0
.borrow()
.as_ref()
.map_or(0, |leader| leader.generation)
}
fn clear_if_owned_by(&self, owner: &PublisherIdentity) {
let mut state = self.0.borrow_mut();
if state
.as_ref()
.is_some_and(|leader| leader.owner.same_owner(owner))
{
*state = None;
}
}
fn clear_publication(&self) {
*self.0.borrow_mut() = None;
}
fn publish(&self, owner: PublisherIdentity, transform: Transform, size: Size, generation: u64) {
let mut state = self.0.borrow_mut();
if state.is_none() {
*state = Some(LeaderData {
owner,
transform,
size,
generation,
});
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FlattenedAnnotation {
pub annotation: Annotation,
pub world_bounds: Option<Rect>,
}
#[derive(Clone, Debug)]
pub enum LayerKind {
Picture {
display_list: Arc<DisplayList>,
bounds: Rect,
},
Transform {
transform: Transform,
},
ClipRect {
rect: Rect,
},
ClipRRect {
rrect: RRect,
},
ClipOval {
rect: Rect,
},
ClipPath {
path: Arc<Path>,
fill_rule: FillRule,
},
Opacity {
alpha: f32,
},
Blur {
blur: GaussianBlur,
},
DropShadow {
shadow: DropShadowEffect,
},
ColorFilter {
filter: Box<ColorFilter>,
},
Blend {
mode: BlendMode,
},
ShaderMask {
shader: Box<Brush>,
blend_mode: BlendMode,
mask_size: Size,
mask_transform: Box<Transform>,
},
BackdropFilter {
blur: GaussianBlur,
blend_mode: BlendMode,
enabled: bool,
matrix: Option<Transform>,
backdrop_key: Option<BackdropKey>,
},
AnnotatedRegion {
annotation: Annotation,
sized: bool,
size: Size,
},
Leader {
link: LayerLink,
size: Size,
publisher: PublisherIdentity,
},
Follower(FollowerLayer),
}
#[derive(Clone, Debug, PartialEq)]
pub struct FollowerLayer {
pub link: LayerLink,
pub show_when_unlinked: bool,
pub offset: Offset,
pub target_anchor: LayerAnchor,
pub follower_anchor: LayerAnchor,
pub size: Size,
}
#[derive(Clone, Debug)]
struct Layer {
kind: LayerKind,
children: Vec<LayerId>,
surface_partition: Option<SurfacePartitionId>,
generation: u64,
clip_cache: Option<Box<ResolvedClipPath>>,
parent: Option<LayerId>,
cull: CullBounds,
}
#[derive(Clone, Copy, Debug)]
enum CullBounds {
Unknown,
Known {
bounds: Option<Rect>,
cullable: bool,
},
}
fn next_generation(counter: &mut u64) -> u64 {
let generation = *counter;
*counter = generation
.checked_add(1)
.expect("layer generation exhausted");
generation
}
pub struct LayerTree {
layers: Arena<Layer>,
root: Option<LayerId>,
diagnostics: CompositorDiagnostics,
flattened_pictures: Vec<FlattenedPicture>,
flattened_annotations: Vec<FlattenedAnnotation>,
next_generation: u64,
annotation_layers: usize,
leader_layers: usize,
partition_layers: usize,
child_stack: Vec<LayerId>,
flatten_scratch: DisplayList,
flatten_published: DisplayList,
}
impl Default for LayerTree {
fn default() -> Self {
Self::new()
}
}
impl LayerTree {
#[must_use]
pub fn new() -> Self {
Self {
layers: Arena::new(),
root: None,
diagnostics: CompositorDiagnostics::default(),
flattened_pictures: Vec::new(),
flattened_annotations: Vec::new(),
next_generation: 1,
annotation_layers: 0,
leader_layers: 0,
partition_layers: 0,
child_stack: Vec::new(),
flatten_scratch: DisplayList::new(),
flatten_published: DisplayList::new(),
}
}
#[must_use]
pub fn diagnostics(&self) -> CompositorDiagnostics {
self.diagnostics
}
#[must_use]
pub fn root(&self) -> Option<LayerId> {
self.root
}
#[must_use]
pub fn layer(&self, id: LayerId) -> Option<(&LayerKind, &[LayerId])> {
self.layers
.get(id.0)
.map(|layer| (&layer.kind, layer.children.as_slice()))
}
#[doc(hidden)]
#[must_use]
pub fn contains(&self, id: LayerId) -> bool {
self.layers.contains(id.0)
}
pub fn set_root(&mut self, root: LayerId) {
self.root = Some(root);
}
#[must_use]
pub fn scene_bounds(&self) -> Option<Rect> {
self.root
.and_then(|root| self.visual_subtree_bounds(root, Transform::IDENTITY))
}
pub fn create_picture(&mut self, display_list: DisplayList, bounds: Rect) -> LayerId {
self.insert(LayerKind::Picture {
display_list: Arc::new(display_list),
bounds,
})
}
pub fn create_transform(&mut self, transform: Transform) -> LayerId {
self.insert(LayerKind::Transform { transform })
}
pub fn create_clip_rect(&mut self, rect: Rect) -> LayerId {
self.insert(LayerKind::ClipRect { rect })
}
pub fn create_clip_rrect(&mut self, rrect: RRect) -> LayerId {
self.insert(LayerKind::ClipRRect { rrect })
}
pub fn create_clip_oval(&mut self, rect: Rect) -> LayerId {
self.insert(LayerKind::ClipOval { rect })
}
pub fn create_clip_path(&mut self, path: Arc<Path>, fill_rule: FillRule) -> LayerId {
self.insert(LayerKind::ClipPath { path, fill_rule })
}
pub fn create_opacity(&mut self, alpha: f32) -> LayerId {
self.insert(LayerKind::Opacity {
alpha: normalize_opacity(alpha),
})
}
pub fn create_blur(&mut self, blur: GaussianBlur) -> LayerId {
self.insert(LayerKind::Blur {
blur: GaussianBlur::new(blur.sigma_x, blur.sigma_y),
})
}
pub fn create_drop_shadow(&mut self, shadow: DropShadowEffect) -> LayerId {
self.insert(LayerKind::DropShadow {
shadow: DropShadowEffect::asymmetric(
shadow.offset,
shadow.sigma_x,
shadow.sigma_y,
shadow.color,
),
})
}
pub fn create_color_filter(&mut self, filter: ColorFilter) -> LayerId {
self.insert(LayerKind::ColorFilter {
filter: Box::new(filter),
})
}
pub fn create_blend(&mut self, mode: BlendMode) -> LayerId {
self.insert(LayerKind::Blend { mode })
}
pub fn create_shader_mask(
&mut self,
shader: Brush,
blend_mode: BlendMode,
mask_size: Size,
mask_transform: Transform,
) -> LayerId {
self.insert(LayerKind::ShaderMask {
shader: Box::new(shader),
blend_mode,
mask_size,
mask_transform: Box::new(mask_transform),
})
}
pub fn create_backdrop_filter(
&mut self,
blur: GaussianBlur,
blend_mode: BlendMode,
enabled: bool,
) -> LayerId {
self.insert(LayerKind::BackdropFilter {
blur: GaussianBlur::new(blur.sigma_x, blur.sigma_y),
blend_mode,
enabled,
matrix: None,
backdrop_key: None,
})
}
pub fn update_backdrop_matrix(&mut self, id: LayerId, matrix: Option<Transform>) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::BackdropFilter {
matrix: current, ..
} = &mut layer.kind
else {
return false;
};
if *current == matrix {
return false;
}
*current = matrix;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn update_backdrop_key(&mut self, id: LayerId, key: Option<BackdropKey>) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::BackdropFilter {
backdrop_key: current,
..
} = &mut layer.kind
else {
return false;
};
if *current == key {
return false;
}
*current = key;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn create_annotated_region(
&mut self,
annotation: Annotation,
sized: bool,
size: Size,
) -> LayerId {
self.insert(LayerKind::AnnotatedRegion {
annotation,
sized,
size,
})
}
pub fn create_leader(&mut self, link: LayerLink, size: Size) -> LayerId {
self.insert(LayerKind::Leader {
link,
size,
publisher: PublisherIdentity::new(),
})
}
pub fn create_follower(&mut self, follower: FollowerLayer) -> LayerId {
self.insert(LayerKind::Follower(follower))
}
fn insert(&mut self, kind: LayerKind) -> LayerId {
self.annotation_layers += usize::from(matches!(kind, LayerKind::AnnotatedRegion { .. }));
self.leader_layers += usize::from(matches!(kind, LayerKind::Leader { .. }));
let generation = next_generation(&mut self.next_generation);
let id = LayerId(self.layers.insert(Layer {
kind,
children: Vec::new(),
surface_partition: None,
generation,
clip_cache: None,
parent: None,
cull: CullBounds::Unknown,
}));
self.diagnostics.layers += 1;
id
}
pub fn remove(&mut self, id: LayerId) {
if let Some(layer) = self.layers.remove(id.0) {
self.flatten_published = DisplayList::new();
self.annotation_layers -=
usize::from(matches!(layer.kind, LayerKind::AnnotatedRegion { .. }));
self.leader_layers -= usize::from(matches!(layer.kind, LayerKind::Leader { .. }));
if let LayerKind::Leader {
link, publisher, ..
} = &layer.kind
{
link.clear_if_owned_by(publisher);
}
self.diagnostics.layers -= 1;
self.partition_layers -= usize::from(layer.surface_partition.is_some());
if let Some(parent) = layer.parent {
self.reset_cull_bounds(parent);
}
if self.root == Some(id) {
self.root = None;
}
}
}
pub fn set_children(&mut self, id: LayerId, children: Vec<LayerId>) {
let Some(layer) = self.layers.get_mut(id.0) else {
return;
};
if layer.children == children {
return;
}
let previous = std::mem::replace(&mut layer.children, children);
layer.generation = next_generation(&mut self.next_generation);
for child in previous {
if let Some(child) = self.layers.get_mut(child.0)
&& child.parent == Some(id)
{
child.parent = None;
}
}
for index in 0.. {
let Some(child) = self
.layers
.get(id.0)
.and_then(|layer| layer.children.get(index).copied())
else {
break;
};
let Some(child) = self.layers.get_mut(child.0) else {
continue;
};
if let Some(old) = child.parent.replace(id).filter(|old| *old != id) {
self.reset_cull_bounds(old);
}
}
self.reset_cull_bounds(id);
}
pub fn set_surface_partition(&mut self, id: LayerId, partition: Option<SurfacePartitionId>) {
let Some(layer) = self.layers.get_mut(id.0) else {
return;
};
if layer.surface_partition == partition {
return;
}
let had_partition = layer.surface_partition.is_some();
layer.surface_partition = partition;
if had_partition != partition.is_some() {
if had_partition {
self.partition_layers -= 1;
} else {
self.partition_layers += 1;
}
self.reset_cull_bounds(id);
}
}
pub fn clear_surface_partitions(&mut self) {
if self.partition_layers == 0 {
return;
}
let ids = self
.layers
.iter()
.filter_map(|(raw, layer)| layer.surface_partition.is_some().then_some(LayerId(raw)))
.collect::<Vec<_>>();
for id in ids {
self.set_surface_partition(id, None);
}
}
pub fn update_picture(&mut self, id: LayerId, display_list: DisplayList, bounds: Rect) {
if let Some(layer) = self.layers.get_mut(id.0)
&& let LayerKind::Picture {
display_list: current,
bounds: current_bounds,
} = &mut layer.kind
{
*current = Arc::new(display_list);
let moved = *current_bounds != bounds;
*current_bounds = bounds;
layer.generation = next_generation(&mut self.next_generation);
self.diagnostics.picture_layers_repainted += 1;
if moved {
self.reset_cull_bounds(id);
}
}
}
pub fn update_picture_if_changed(
&mut self,
id: LayerId,
display_list: &DisplayList,
bounds: Rect,
) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Picture {
display_list: current,
bounds: current_bounds,
} = &mut layer.kind
else {
return false;
};
if **current == *display_list && *current_bounds == bounds {
return false;
}
self.update_picture(id, display_list.clone(), bounds);
true
}
pub fn update_transform(&mut self, id: LayerId, transform: Transform) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Transform { transform: current } = &mut layer.kind else {
return false;
};
if *current == transform {
return false;
}
*current = transform;
layer.generation = next_generation(&mut self.next_generation);
self.diagnostics.transform_updates += 1;
self.reset_cull_bounds(id);
true
}
pub fn update_clip(&mut self, id: LayerId, rect: Rect) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::ClipRect { rect: current } = &mut layer.kind else {
return false;
};
if *current == rect {
return false;
}
*current = rect;
layer.clip_cache = None;
layer.generation = next_generation(&mut self.next_generation);
self.diagnostics.clip_updates += 1;
true
}
pub fn update_clip_rrect(&mut self, id: LayerId, rrect: RRect) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::ClipRRect { rrect: current } = &mut layer.kind else {
return false;
};
if *current == rrect {
return false;
}
*current = rrect;
layer.clip_cache = None;
layer.generation = next_generation(&mut self.next_generation);
self.diagnostics.clip_updates += 1;
true
}
pub fn update_clip_oval(&mut self, id: LayerId, rect: Rect) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::ClipOval { rect: current } = &mut layer.kind else {
return false;
};
if *current == rect {
return false;
}
*current = rect;
layer.clip_cache = None;
layer.generation = next_generation(&mut self.next_generation);
self.diagnostics.clip_updates += 1;
true
}
pub fn update_clip_path(&mut self, id: LayerId, path: Arc<Path>, fill_rule: FillRule) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::ClipPath {
path: current_path,
fill_rule: current_rule,
} = &mut layer.kind
else {
return false;
};
if **current_path == *path && *current_rule == fill_rule {
return false;
}
*current_path = path;
*current_rule = fill_rule;
layer.clip_cache = None;
layer.generation = next_generation(&mut self.next_generation);
self.diagnostics.clip_updates += 1;
true
}
pub fn update_opacity(&mut self, id: LayerId, alpha: f32) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Opacity { alpha: current } = &mut layer.kind else {
return false;
};
let alpha = normalize_opacity(alpha);
if *current == alpha {
return false;
}
*current = alpha;
self.diagnostics.opacity_updates += 1;
true
}
pub fn update_blur(&mut self, id: LayerId, blur: GaussianBlur) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Blur { blur: current } = &mut layer.kind else {
return false;
};
let blur = GaussianBlur::new(blur.sigma_x, blur.sigma_y);
if *current == blur {
return false;
}
*current = blur;
true
}
pub fn update_drop_shadow(&mut self, id: LayerId, shadow: DropShadowEffect) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::DropShadow { shadow: current } = &mut layer.kind else {
return false;
};
let shadow = DropShadowEffect::asymmetric(
shadow.offset,
shadow.sigma_x,
shadow.sigma_y,
shadow.color,
);
if *current == shadow {
return false;
}
*current = shadow;
true
}
pub fn update_color_filter(&mut self, id: LayerId, filter: ColorFilter) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::ColorFilter { filter: current } = &mut layer.kind else {
return false;
};
if **current == filter {
return false;
}
**current = filter;
true
}
pub fn update_blend(&mut self, id: LayerId, mode: BlendMode) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Blend { mode: current } = &mut layer.kind else {
return false;
};
if *current == mode {
return false;
}
*current = mode;
true
}
pub fn update_shader_mask(
&mut self,
id: LayerId,
shader: Brush,
blend_mode: BlendMode,
mask_size: Size,
mask_transform: Transform,
) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::ShaderMask {
shader: current_shader,
blend_mode: current_mode,
mask_size: current_size,
mask_transform: current_transform,
} = &mut layer.kind
else {
return false;
};
if **current_shader == shader
&& *current_mode == blend_mode
&& *current_size == mask_size
&& **current_transform == mask_transform
{
return false;
}
**current_shader = shader;
*current_mode = blend_mode;
*current_size = mask_size;
**current_transform = mask_transform;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn update_shader_mask_blend(&mut self, id: LayerId, blend_mode: BlendMode) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::ShaderMask {
blend_mode: current,
..
} = &mut layer.kind
else {
return false;
};
if *current == blend_mode {
return false;
}
*current = blend_mode;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn update_backdrop_filter(
&mut self,
id: LayerId,
blur: GaussianBlur,
blend_mode: BlendMode,
enabled: bool,
) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::BackdropFilter {
blur: current_blur,
blend_mode: current_mode,
enabled: current_enabled,
..
} = &mut layer.kind
else {
return false;
};
let blur = GaussianBlur::new(blur.sigma_x, blur.sigma_y);
if *current_blur == blur && *current_mode == blend_mode && *current_enabled == enabled {
return false;
}
*current_blur = blur;
*current_mode = blend_mode;
*current_enabled = enabled;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn update_annotated_region(
&mut self,
id: LayerId,
annotation: Annotation,
sized: bool,
size: Size,
) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::AnnotatedRegion {
annotation: current_annotation,
sized: current_sized,
size: current_size,
} = &mut layer.kind
else {
return false;
};
if *current_annotation == annotation && *current_sized == sized && *current_size == size {
return false;
}
*current_annotation = annotation;
*current_sized = sized;
*current_size = size;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn update_leader(&mut self, id: LayerId, link: LayerLink, size: Size) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Leader {
link: current_link,
size: current_size,
publisher,
} = &mut layer.kind
else {
return false;
};
if *current_link == link && *current_size == size {
return false;
}
current_link.clear_if_owned_by(publisher);
*current_link = link;
*current_size = size;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn update_leader_size(&mut self, id: LayerId, size: Size) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Leader { size: current, .. } = &mut layer.kind else {
return false;
};
if *current == size {
return false;
}
*current = size;
layer.generation = next_generation(&mut self.next_generation);
true
}
pub fn update_follower(&mut self, id: LayerId, follower: FollowerLayer) -> bool {
let Some(layer) = self.layers.get_mut(id.0) else {
return false;
};
let LayerKind::Follower(current) = &mut layer.kind else {
return false;
};
if *current == follower {
return false;
}
*current = follower;
layer.generation = next_generation(&mut self.next_generation);
true
}
#[must_use]
pub fn flattened_pictures(&self) -> &[FlattenedPicture] {
&self.flattened_pictures
}
#[must_use]
pub fn flattened_annotations(&self) -> &[FlattenedAnnotation] {
&self.flattened_annotations
}
#[must_use]
pub fn find_annotation<T: Any + Clone>(&self, point: Offset) -> Option<T> {
self.flattened_annotations.iter().find_map(|entry| {
let in_bounds = entry
.world_bounds
.is_none_or(|bounds| bounds.contains(point));
in_bounds.then(|| entry.annotation.cloned::<T>()).flatten()
})
}
#[must_use]
pub fn find_annotations<T: Any + Clone>(&self, point: Offset) -> Vec<T> {
self.flattened_annotations
.iter()
.filter_map(|entry| {
let in_bounds = entry
.world_bounds
.is_none_or(|bounds| bounds.contains(point));
in_bounds.then(|| entry.annotation.cloned::<T>()).flatten()
})
.collect()
}
#[must_use]
pub fn flatten(&mut self) -> DisplayList {
let mut out = std::mem::take(&mut self.flatten_scratch);
out.clear();
self.flattened_pictures.clear();
self.flattened_annotations.clear();
if let Some(root) = self.root {
self.publish_leader_links();
self.flatten_layer(root, Transform::IDENTITY, None, &mut out);
if self.annotation_layers > 0 {
self.collect_annotations(root, Transform::IDENTITY, None);
}
}
if out != self.flatten_published {
self.flatten_published = out.deep_copy();
}
out.clear();
self.flatten_scratch = out;
self.flatten_published.clone()
}
fn clip_command(&mut self, id: LayerId, kind: &LayerKind, world: Transform) -> PaintCommand {
match kind {
LayerKind::ClipRect { rect, .. } => match clip_space(world) {
ClipSpace::Translation | ClipSpace::Scale { .. } => PaintCommand::PushClip {
rect: world.transform_rect_bbox(*rect),
},
ClipSpace::General => {
let rect = *rect;
PaintCommand::PushClipPath {
path: self.memoized_fallback_path(id, move || rect_as_path(rect), world),
fill_rule: FillRule::NonZero,
}
}
},
LayerKind::ClipRRect { rrect, .. } => match clip_space(world) {
ClipSpace::Translation => PaintCommand::PushClipRRect {
rrect: RRect::new(world.transform_rect_bbox(rrect.rect), rrect.radii),
},
ClipSpace::Scale { x, y } if x > 0. && x == y => PaintCommand::PushClipRRect {
rrect: RRect::new(world.transform_rect_bbox(rrect.rect), rrect.radii.scaled(x)),
},
_ => {
let (rect, radii) = (rrect.rect, rrect.radii);
PaintCommand::PushClipPath {
path: self.memoized_fallback_path(
id,
move || rrect_as_path(rect, radii, fallback_tolerance(world)),
world,
),
fill_rule: FillRule::NonZero,
}
}
},
LayerKind::ClipOval { rect, .. } => match clip_space(world) {
ClipSpace::Translation | ClipSpace::Scale { .. } => PaintCommand::PushClipOval {
rect: world.transform_rect_bbox(*rect),
},
ClipSpace::General => {
let rect = *rect;
PaintCommand::PushClipPath {
path: self.memoized_fallback_path(
id,
move || ellipse_as_path(rect, fallback_tolerance(world)),
world,
),
fill_rule: FillRule::NonZero,
}
}
},
LayerKind::ClipPath {
path, fill_rule, ..
} => PaintCommand::PushClipPath {
path: self.memoized_clip_path(id, path, world),
fill_rule: *fill_rule,
},
_ => unreachable!("clip emitter received a non-clip layer"),
}
}
fn memoized_clip_path(&mut self, id: LayerId, path: &Arc<Path>, world: Transform) -> Arc<Path> {
if world == Transform::IDENTITY {
return path.clone();
}
let owned = path.clone();
self.memoized_fallback_path(id, move || owned.as_ref().clone(), world)
}
fn memoized_fallback_path(
&mut self,
id: LayerId,
build: impl FnOnce() -> Path,
world: Transform,
) -> Arc<Path> {
if let Some(layer) = self.layers.get(id.0)
&& let Some(memo) = layer.clip_cache.as_ref()
&& memo.world == world
{
return memo.path.clone();
}
let resolved = Arc::new(build().transformed(world));
if let Some(layer) = self.layers.get_mut(id.0) {
layer.clip_cache = Some(Box::new(ResolvedClipPath {
world,
path: resolved.clone(),
}));
}
resolved
}
fn reset_cull_bounds(&mut self, id: LayerId) {
let mut current = Some(id);
while let Some(layer) = current.and_then(|id| self.layers.get_mut(id.0)) {
if matches!(layer.cull, CullBounds::Unknown) {
break;
}
layer.cull = CullBounds::Unknown;
current = layer.parent;
}
}
fn cull_bounds(&mut self, id: LayerId) -> (Option<Rect>, bool) {
let Some(layer) = self.layers.get(id.0) else {
return (None, true);
};
if let CullBounds::Known { bounds, cullable } = layer.cull {
return (bounds, cullable);
}
let own_cullable = layer.surface_partition.is_none()
&& !matches!(
layer.kind,
LayerKind::Follower(_) | LayerKind::BackdropFilter { .. }
);
let (local, own_bounds) = match &layer.kind {
LayerKind::Picture { bounds, .. } => (Transform::IDENTITY, Some(*bounds)),
LayerKind::Transform { transform } => (*transform, None),
_ => (Transform::IDENTITY, None),
};
let mut bounds = own_bounds;
let mut cullable = own_cullable;
for index in 0.. {
let Some(child) = self
.layers
.get(id.0)
.and_then(|layer| layer.children.get(index).copied())
else {
break;
};
let (child_bounds, child_cullable) = self.cull_bounds(child);
cullable &= child_cullable;
if let Some(child_bounds) = child_bounds {
let child_bounds = local.transform_rect_bbox(child_bounds);
bounds =
Some(bounds.map_or(child_bounds, |bounds| union_rect(bounds, child_bounds)));
}
}
if let Some(layer) = self.layers.get_mut(id.0) {
layer.cull = CullBounds::Known { bounds, cullable };
}
(bounds, cullable)
}
fn flatten_layer(
&mut self,
id: LayerId,
world_transform: Transform,
clip: Option<Rect>,
out: &mut DisplayList,
) {
if let Some(active) = clip {
let (bounds, cullable) = self.cull_bounds(id);
if cullable
&& bounds.is_none_or(|bounds| {
!active.intersects(world_transform.transform_rect_bbox(bounds))
})
{
self.diagnostics.layers_culled += 1;
return;
}
}
let partition = self
.layers
.get(id.0)
.and_then(|layer| layer.surface_partition);
if let Some(id) = partition {
out.push(PaintCommand::PushSurfacePartition { id });
}
self.flatten_layer_contents(id, world_transform, clip, out);
if partition.is_some() {
out.push(PaintCommand::PopSurfacePartition);
}
}
fn visit_children(
&mut self,
id: LayerId,
reverse: bool,
mut visit: impl FnMut(&mut Self, LayerId),
) {
let start = self.child_stack.len();
if let Some(layer) = self.layers.get(id.0) {
self.child_stack.extend_from_slice(&layer.children);
}
let end = self.child_stack.len();
for index in start..end {
let child = self.child_stack[if reverse {
start + end - 1 - index
} else {
index
}];
visit(self, child);
}
self.child_stack.truncate(start);
}
fn flatten_layer_contents(
&mut self,
id: LayerId,
world_transform: Transform,
clip: Option<Rect>,
out: &mut DisplayList,
) {
let Some(kind) = self.layers.get(id.0).map(|layer| layer.kind.clone()) else {
return;
};
match kind {
LayerKind::Picture {
display_list,
bounds,
} => {
let world = world_transform.transform_rect_bbox(bounds);
if clip.is_some_and(|active| !active.intersects(world)) {
self.diagnostics.layers_culled += 1;
return;
}
self.flattened_pictures.push(FlattenedPicture {
layer: id,
local_bounds: bounds,
world_bounds: world,
active_clip: clip,
});
out.push(PaintCommand::PushTransform {
transform: world_transform,
});
out.extend_from(&display_list);
out.push(PaintCommand::PopTransform);
}
LayerKind::Transform { transform: local } => {
let next = world_transform.then(local);
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, next, clip, out)
});
}
LayerKind::ClipRect { .. }
| LayerKind::ClipRRect { .. }
| LayerKind::ClipOval { .. }
| LayerKind::ClipPath { .. } => {
let Some(shape_bounds) = clip_world_bounds(&kind, world_transform) else {
self.diagnostics.layers_culled += 1;
return;
};
let next_clip = match clip {
Some(active) => active.intersection(shape_bounds),
None => Some(shape_bounds),
};
if next_clip.is_none() {
self.diagnostics.layers_culled += 1;
return;
}
out.push(self.clip_command(id, &kind, world_transform));
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, next_clip, out)
});
out.push(PaintCommand::PopClip);
}
LayerKind::Opacity { alpha } => {
let bounds = self
.subtree_bounds(id, world_transform)
.unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
out.push(PaintCommand::PushOpacity {
layer: id,
alpha,
generation: self.subtree_generation(id),
bounds,
});
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
out.push(PaintCommand::PopOpacity);
}
LayerKind::Blur { blur } => {
let bounds = self
.subtree_bounds(id, world_transform)
.unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
out.push(PaintCommand::PushBlur {
layer: id,
blur,
generation: self.subtree_generation(id),
bounds,
});
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
out.push(PaintCommand::PopEffect);
}
LayerKind::DropShadow { shadow } => {
let bounds = self
.subtree_bounds(id, world_transform)
.unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
out.push(PaintCommand::PushDropShadow {
layer: id,
shadow,
generation: self.subtree_generation(id),
bounds,
});
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
out.push(PaintCommand::PopEffect);
}
LayerKind::ColorFilter { filter } => {
let bounds = self
.subtree_bounds(id, world_transform)
.unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
out.push(PaintCommand::PushColorFilter {
layer: id,
filter: *filter,
generation: self.subtree_generation(id),
bounds,
});
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
out.push(PaintCommand::PopEffect);
}
LayerKind::Blend { mode } => {
let bounds = self
.subtree_bounds(id, world_transform)
.unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
out.push(PaintCommand::PushBlend {
layer: id,
mode,
generation: self.subtree_generation(id),
bounds,
});
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
out.push(PaintCommand::PopEffect);
}
LayerKind::ShaderMask {
shader,
blend_mode,
mask_size,
mask_transform,
} => {
let bounds = self
.subtree_bounds(id, world_transform)
.unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
if !bounds.size.width.is_finite()
|| !bounds.size.height.is_finite()
|| bounds.size.width <= 0.
|| bounds.size.height <= 0.
{
return;
}
out.push(PaintCommand::PushShaderMask {
layer: id,
shader: *shader,
blend_mode,
mask_size,
mask_transform: *mask_transform,
generation: self.subtree_generation(id),
bounds,
});
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
out.push(PaintCommand::PopEffect);
}
LayerKind::BackdropFilter {
blur,
blend_mode,
enabled,
matrix,
backdrop_key,
} => {
if !crate::backdrop_filter_is_active(blur, enabled, matrix) {
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
return;
}
let bounds = self
.subtree_bounds(id, world_transform)
.unwrap_or_else(|| Rect::from_origin_size(Offset::ZERO, Size::ZERO));
out.push(PaintCommand::PushBackdropFilter {
layer: id,
blur,
blend_mode,
enabled,
matrix,
backdrop_key,
generation: self.subtree_generation(id),
bounds,
});
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
out.push(PaintCommand::PopEffect);
}
LayerKind::AnnotatedRegion { .. } => {
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
}
LayerKind::Leader { .. } => {
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, world_transform, clip, out)
});
}
LayerKind::Follower(follower) => {
let Some(next_transform) = self.follower_transform(world_transform, &follower)
else {
return;
};
self.visit_children(id, false, |tree, child| {
tree.flatten_layer(child, next_transform, clip, out)
});
}
}
}
fn clear_link_states(&self, id: LayerId) {
let Some(layer) = self.layers.get(id.0) else {
return;
};
if let LayerKind::Leader { link, .. } = &layer.kind {
link.clear_publication();
}
for child in layer.children.iter() {
self.clear_link_states(*child);
}
}
pub fn publish_leader_links(&mut self) {
if self.leader_layers == 0 {
return;
}
if let Some(root) = self.root {
self.clear_link_states(root);
self.publish_resolved_leaders(root);
}
}
fn publish_resolved_leaders(&mut self, root: LayerId) {
self.diagnostics.leader_publish_passes += 1;
let mut parents = HashMap::new();
let mut leaders = Vec::new();
self.collect_publish_state(root, &mut parents, &mut leaders);
if leaders.is_empty() {
return;
}
let mut states = HashMap::new();
let mut published = 0;
for index in 0..leaders.len() {
published += self.ensure_published(index, &leaders, &parents, &mut states) as u64;
}
self.diagnostics.leaders_published += published;
}
fn collect_publish_state(
&self,
id: LayerId,
parents: &mut HashMap<LayerId, LayerId>,
leaders: &mut Vec<LeaderRecord>,
) {
let Some(layer) = self.layers.get(id.0) else {
return;
};
if let LayerKind::Leader {
link,
size,
publisher,
} = &layer.kind
{
leaders.push(LeaderRecord {
id,
link: link.clone(),
size: *size,
publisher: publisher.clone(),
generation: layer.generation,
});
}
for child in layer.children.iter() {
parents.entry(*child).or_insert(id);
self.collect_publish_state(*child, parents, leaders);
}
}
fn ensure_published(
&self,
index: usize,
leaders: &[LeaderRecord],
parents: &HashMap<LayerId, LayerId>,
states: &mut HashMap<LayerId, PublishState>,
) -> bool {
let id = leaders[index].id;
match states.get(&id) {
Some(PublishState::Published) => return true,
Some(PublishState::Unresolvable) => return false,
Some(PublishState::InProgress) => {
states.insert(id, PublishState::Unresolvable);
return false;
}
None => {}
}
states.insert(id, PublishState::InProgress);
let published = match self.leader_context(index, leaders, parents, states) {
Some(world) => {
let record = &leaders[index];
record.link.publish(
record.publisher.clone(),
world,
record.size,
record.generation,
);
true
}
None => false,
};
states.insert(
id,
if published {
PublishState::Published
} else {
PublishState::Unresolvable
},
);
published
}
fn leader_context(
&self,
index: usize,
leaders: &[LeaderRecord],
parents: &HashMap<LayerId, LayerId>,
states: &mut HashMap<LayerId, PublishState>,
) -> Option<Transform> {
let mut chain = vec![leaders[index].id];
while let Some(parent) = chain.last().and_then(|id| parents.get(id)) {
chain.push(*parent);
}
let mut world = Transform::IDENTITY;
let mut clip: Option<Rect> = None;
for node in chain.iter().rev() {
let layer = self.layers.get(node.0)?;
match &layer.kind {
LayerKind::Transform { transform } => {
world = world.then(*transform);
}
LayerKind::Follower(follower) => {
let link = &follower.link;
if link.leader_transform().is_none() {
for dependency in 0..leaders.len() {
if leaders[dependency].link == *link {
self.ensure_published(dependency, leaders, parents, states);
if link.leader_transform().is_some() {
break;
}
}
}
}
match link.leader_transform() {
Some(_) => {
world = self.follower_transform(world, follower)?;
}
None if follower.show_when_unlinked => {}
None => return None,
}
}
LayerKind::ClipRect { .. }
| LayerKind::ClipRRect { .. }
| LayerKind::ClipOval { .. }
| LayerKind::ClipPath { .. } => {
let shape = clip_world_bounds(&layer.kind, world)?;
clip = match clip {
Some(active) => active.intersection(shape),
None => Some(shape),
};
clip?;
}
_ => {}
}
}
Some(world)
}
fn follower_transform(
&self,
world_transform: Transform,
follower: &FollowerLayer,
) -> Option<Transform> {
let FollowerLayer {
link,
show_when_unlinked,
offset,
target_anchor,
follower_anchor,
size,
} = follower;
let Some(leader_transform) = link.leader_transform() else {
return show_when_unlinked.then_some(world_transform);
};
let leader_size = link.leader_size().unwrap_or(Size::ZERO);
let desired_anchor =
resolve_follower_target(leader_transform, leader_size, *target_anchor, *offset);
let desired_local = world_transform.inverse_transform_point(desired_anchor)?;
let local_delta = desired_local - follower_anchor.along_size(*size);
Some(world_transform.then(Transform::translation(local_delta)))
}
fn collect_annotations(&mut self, id: LayerId, world_transform: Transform, clip: Option<Rect>) {
let Some(kind) = self.layers.get(id.0).map(|layer| layer.kind.clone()) else {
return;
};
match kind {
LayerKind::Transform { transform } => {
let next = world_transform.then(transform);
self.visit_children(id, false, |tree, child| {
tree.collect_annotations(child, next, clip)
});
}
LayerKind::ClipRect { rect, .. } => {
let world = world_transform.transform_rect_bbox(rect);
let next_clip = match clip {
Some(current) => current.intersection(world),
None => Some(world),
};
if next_clip.is_none() {
return;
}
self.visit_children(id, false, |tree, child| {
tree.collect_annotations(child, world_transform, next_clip)
});
}
LayerKind::ClipRRect { .. }
| LayerKind::ClipOval { .. }
| LayerKind::ClipPath { .. } => {
let Some(world) = clip_world_bounds(&kind, world_transform) else {
return;
};
let next_clip = match clip {
Some(current) => current.intersection(world),
None => Some(world),
};
if next_clip.is_none() {
return;
}
self.visit_children(id, false, |tree, child| {
tree.collect_annotations(child, world_transform, next_clip)
});
}
LayerKind::Follower(follower) => {
let Some(next) = self.follower_transform(world_transform, &follower) else {
return;
};
self.visit_children(id, false, |tree, child| {
tree.collect_annotations(child, next, clip)
});
}
LayerKind::AnnotatedRegion {
annotation,
sized,
size,
} => {
self.visit_children(id, true, |tree, child| {
tree.collect_annotations(child, world_transform, clip)
});
let region =
world_transform.transform_rect_bbox(Rect::from_origin_size(Offset::ZERO, size));
let world_bounds = if sized {
match clip {
Some(active) => active.intersection(region),
None => Some(region),
}
} else {
clip
};
if world_bounds
.is_none_or(|bounds| bounds.size.width > 0. && bounds.size.height > 0.)
{
self.flattened_annotations.push(FlattenedAnnotation {
annotation,
world_bounds,
});
}
}
LayerKind::Picture { .. }
| LayerKind::Opacity { .. }
| LayerKind::Blur { .. }
| LayerKind::DropShadow { .. }
| LayerKind::ColorFilter { .. }
| LayerKind::Blend { .. }
| LayerKind::ShaderMask { .. }
| LayerKind::BackdropFilter { .. }
| LayerKind::Leader { .. } => {
self.visit_children(id, false, |tree, child| {
tree.collect_annotations(child, world_transform, clip)
});
}
}
}
fn subtree_generation(&self, id: LayerId) -> u64 {
let Some(layer) = self.layers.get(id.0) else {
return 0;
};
let mut value = layer.generation.wrapping_mul(0x9e37_79b9_7f4a_7c15);
for child in &layer.children {
if let Some(child_layer) = self.layers.get(child.0)
&& let LayerKind::Opacity { alpha } = &child_layer.kind
{
value = value.rotate_left(11) ^ u64::from(alpha.to_bits());
}
if let Some(child_layer) = self.layers.get(child.0) {
match &child_layer.kind {
LayerKind::Blur { blur } => {
value = value.rotate_left(13)
^ u64::from(blur.sigma_x.to_bits())
^ u64::from(blur.sigma_y.to_bits()).rotate_left(17);
}
LayerKind::DropShadow { shadow } => {
value = value.rotate_left(13)
^ u64::from(shadow.offset.x.to_bits())
^ u64::from(shadow.offset.y.to_bits()).rotate_left(7)
^ u64::from(shadow.sigma_x.to_bits()).rotate_left(17)
^ u64::from(shadow.sigma_y.to_bits()).rotate_left(23)
^ u64::from(shadow.color.red)
^ (u64::from(shadow.color.green) << 8)
^ (u64::from(shadow.color.blue) << 16)
^ (u64::from(shadow.color.alpha) << 24);
}
LayerKind::ColorFilter { filter } => {
for (index, bits) in filter.to_matrix().map(f32::to_bits).iter().enumerate()
{
value ^= u64::from(*bits).rotate_left((index as u32 % 31) + 1);
}
}
LayerKind::Blend { mode } => {
value = value.rotate_left(13) ^ u64::from(mode.code());
}
LayerKind::ShaderMask {
shader,
blend_mode,
mask_size,
mask_transform,
} => {
value = value.rotate_left(13)
^ brush_generation(shader)
^ u64::from(blend_mode.code())
^ u64::from(mask_size.width.to_bits()).rotate_left(9)
^ u64::from(mask_size.height.to_bits()).rotate_left(15)
^ transform_generation(**mask_transform);
}
LayerKind::BackdropFilter {
blur,
blend_mode,
enabled,
matrix,
backdrop_key: _,
} => {
value = value.rotate_left(13)
^ u64::from(blur.sigma_x.to_bits())
^ u64::from(blur.sigma_y.to_bits()).rotate_left(7)
^ u64::from(blend_mode.code())
^ u64::from(*enabled as u8);
if let Some(matrix) = matrix {
value ^= transform_generation(*matrix).rotate_left(21);
}
}
LayerKind::AnnotatedRegion {
annotation: _,
sized,
size,
} => {
value = value.rotate_left(13)
^ u64::from(*sized as u8)
^ u64::from(size.width.to_bits()).rotate_left(9)
^ u64::from(size.height.to_bits()).rotate_left(15);
}
LayerKind::Leader { link, size, .. } => {
value = value.rotate_left(13)
^ link.leader_generation()
^ u64::from(size.width.to_bits()).rotate_left(9)
^ u64::from(size.height.to_bits()).rotate_left(15);
}
LayerKind::Follower(follower) => {
value = value.rotate_left(13)
^ follower.link.leader_generation()
^ u64::from(follower.show_when_unlinked as u8)
^ u64::from(follower.offset.x.to_bits())
^ u64::from(follower.offset.y.to_bits()).rotate_left(7)
^ u64::from(follower.target_anchor.x.to_bits()).rotate_left(13)
^ u64::from(follower.target_anchor.y.to_bits()).rotate_left(17)
^ u64::from(follower.follower_anchor.x.to_bits()).rotate_left(21)
^ u64::from(follower.follower_anchor.y.to_bits()).rotate_left(25)
^ u64::from(follower.size.width.to_bits()).rotate_left(29)
^ u64::from(follower.size.height.to_bits()).rotate_left(31);
}
_ => {}
}
}
value = value.rotate_left(7) ^ self.subtree_generation(*child);
}
value
}
fn subtree_bounds(&self, id: LayerId, world_transform: Transform) -> Option<Rect> {
let layer = self.layers.get(id.0)?;
match &layer.kind {
LayerKind::Picture { bounds, .. } => Some(world_transform.transform_rect_bbox(*bounds)),
LayerKind::Transform { transform: local } => layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, world_transform.then(*local)))
.reduce(union_rect),
LayerKind::ClipRect { rect, .. } => {
let world = world_transform.transform_rect_bbox(*rect);
layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, world_transform))
.reduce(union_rect)
.and_then(|bounds| bounds.intersection(world))
}
LayerKind::ClipRRect { .. }
| LayerKind::ClipOval { .. }
| LayerKind::ClipPath { .. } => {
let world = clip_world_bounds(&layer.kind, world_transform)?;
layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, world_transform))
.reduce(union_rect)
.and_then(|bounds| bounds.intersection(world))
}
LayerKind::Opacity { .. } => layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, world_transform))
.reduce(union_rect),
LayerKind::Blur { .. } | LayerKind::DropShadow { .. } => layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, world_transform))
.reduce(union_rect),
LayerKind::ColorFilter { .. } | LayerKind::Blend { .. } => layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, world_transform))
.reduce(union_rect),
LayerKind::ShaderMask { .. }
| LayerKind::BackdropFilter { .. }
| LayerKind::AnnotatedRegion { .. }
| LayerKind::Leader { .. } => layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, world_transform))
.reduce(union_rect),
LayerKind::Follower(follower) => {
let next = self.follower_transform(world_transform, follower)?;
layer
.children
.iter()
.filter_map(|child| self.subtree_bounds(*child, next))
.reduce(union_rect)
}
}
}
fn visual_subtree_bounds(&self, id: LayerId, world_transform: Transform) -> Option<Rect> {
let layer = self.layers.get(id.0)?;
let child_bounds = |tree: &Self, transform: Transform| {
layer
.children
.iter()
.filter_map(|child| tree.visual_subtree_bounds(*child, transform))
.reduce(union_rect)
};
match &layer.kind {
LayerKind::Picture { bounds, .. } => Some(world_transform.transform_rect_bbox(*bounds)),
LayerKind::Transform { transform: local } => {
child_bounds(self, world_transform.then(*local))
}
LayerKind::ClipRect { rect, .. } => {
let world = world_transform.transform_rect_bbox(*rect);
child_bounds(self, world_transform).and_then(|bounds| bounds.intersection(world))
}
LayerKind::ClipRRect { .. }
| LayerKind::ClipOval { .. }
| LayerKind::ClipPath { .. } => {
let world = clip_world_bounds(&layer.kind, world_transform)?;
child_bounds(self, world_transform).and_then(|bounds| bounds.intersection(world))
}
LayerKind::Opacity { .. }
| LayerKind::ColorFilter { .. }
| LayerKind::Blend { .. }
| LayerKind::ShaderMask { .. }
| LayerKind::BackdropFilter { .. }
| LayerKind::AnnotatedRegion { .. }
| LayerKind::Leader { .. } => child_bounds(self, world_transform),
LayerKind::Blur { blur } => child_bounds(self, world_transform)
.map(|bounds| blur_bounds(bounds, blur.sigma_x, blur.sigma_y)),
LayerKind::DropShadow { shadow } => child_bounds(self, world_transform).map(|bounds| {
drop_shadow_bounds(bounds, shadow.offset, shadow.sigma_x, shadow.sigma_y)
}),
LayerKind::Follower(follower) => {
let next = self.follower_transform(world_transform, follower)?;
child_bounds(self, next)
}
}
}
#[must_use]
pub fn debug_tree(&self) -> String {
let mut out = String::new();
if let Some(root) = self.root {
self.write_debug(root, 0, &mut out);
}
out
}
fn write_debug(&self, id: LayerId, depth: usize, out: &mut String) {
self.write_debug_at(id, None, depth, Transform::IDENTITY, None, out);
}
fn write_debug_at(
&self,
id: LayerId,
parent: Option<LayerId>,
depth: usize,
world_transform: Transform,
clip: Option<Rect>,
out: &mut String,
) {
let Some(layer) = self.layers.get(id.0) else {
return;
};
let indent = " ".repeat(depth);
let kind = match &layer.kind {
LayerKind::Picture { bounds, .. } => format!(
"Picture(local_bounds={bounds:?}, world_bounds={:?}, clip={clip:?})",
world_transform.transform_rect_bbox(*bounds)
),
LayerKind::Transform { transform } => {
format!(
"Transform(local={:?}, world={:?})",
transform,
world_transform.then(*transform)
)
}
LayerKind::ClipRect { rect, .. } => format!(
"ClipRect(local={rect:?}, world={:?})",
world_transform.transform_rect_bbox(*rect)
),
LayerKind::ClipRRect { rrect, .. } => format!(
"ClipRRect(local={rrect:?}, world={:?})",
world_transform.transform_rect_bbox(rrect.rect)
),
LayerKind::ClipOval { rect, .. } => format!(
"ClipOval(local={rect:?}, world={:?})",
world_transform.transform_rect_bbox(*rect)
),
LayerKind::ClipPath { path, .. } => format!(
"ClipPath(local_bounds={:?}, world={:?})",
path.bounds(),
clip_world_bounds(&layer.kind, world_transform)
),
LayerKind::Opacity { alpha } => format!(
"Opacity(alpha={alpha:.3}, bounds={:?}, generation={})",
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
LayerKind::Blur { blur } => format!(
"Blur(sigma=({:.3},{:.3}), bounds={:?}, generation={})",
blur.sigma_x,
blur.sigma_y,
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
LayerKind::DropShadow { shadow } => format!(
"DropShadow(sigma=({:.3},{:.3}), offset={:?}, bounds={:?}, generation={})",
shadow.sigma_x,
shadow.sigma_y,
shadow.offset,
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
LayerKind::ColorFilter { filter } => format!(
"ColorFilter(matrix={:?}, bounds={:?}, generation={})",
filter.to_matrix(),
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
LayerKind::Blend { mode } => format!(
"Blend(mode={mode:?}, bounds={:?}, generation={})",
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
LayerKind::ShaderMask {
blend_mode,
mask_size,
..
} => format!(
"ShaderMask(blend_mode={blend_mode:?}, mask_size={mask_size:?}, bounds={:?}, generation={})",
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
LayerKind::BackdropFilter {
blur,
blend_mode,
enabled,
matrix,
backdrop_key,
} => format!(
"BackdropFilter({}{}, blend_mode={blend_mode:?}, enabled={enabled}, bounds={:?}, generation={})",
match matrix {
Some(matrix) => format!("matrix={matrix:?}"),
None => format!("sigma=({:.3},{:.3})", blur.sigma_x, blur.sigma_y),
},
backdrop_key.map_or_else(String::new, |key| format!(", key={key:?}")),
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
LayerKind::AnnotatedRegion { sized, size, .. } => format!(
"AnnotatedRegion(sized={sized}, size={size:?}, generation={})",
self.subtree_generation(id)
),
LayerKind::Leader { link, size, .. } => format!(
"CompositedTransformTarget(size={size:?}, linked={}, generation={})",
link.is_linked(),
self.subtree_generation(id)
),
LayerKind::Follower(FollowerLayer {
show_when_unlinked,
offset,
target_anchor,
follower_anchor,
..
}) => format!(
"CompositedTransformFollower(show_when_unlinked={show_when_unlinked}, offset={offset:?}, target_anchor={target_anchor:?}, follower_anchor={follower_anchor:?}, bounds={:?}, generation={})",
self.subtree_bounds(id, world_transform),
self.subtree_generation(id)
),
};
out.push_str(&format!("{indent}{id:?} parent={parent:?} {kind}\n"));
let (next_translation, next_clip) = match &layer.kind {
LayerKind::Picture { .. } => (world_transform, clip),
LayerKind::Transform { transform: local } => (world_transform.then(*local), clip),
LayerKind::ClipRect { rect, .. } => {
let world = world_transform.transform_rect_bbox(*rect);
(
world_transform,
Some(clip.map_or(world, |old| old.intersection(world).unwrap_or(world))),
)
}
LayerKind::ClipRRect { .. }
| LayerKind::ClipOval { .. }
| LayerKind::ClipPath { .. } => {
let narrowed = clip_world_bounds(&layer.kind, world_transform).map(|world| {
clip.map_or(world, |old| old.intersection(world).unwrap_or(world))
});
(world_transform, narrowed.or(clip))
}
LayerKind::Opacity { .. } => (world_transform, clip),
LayerKind::Blur { .. } | LayerKind::DropShadow { .. } => (world_transform, clip),
LayerKind::ColorFilter { .. } | LayerKind::Blend { .. } => (world_transform, clip),
LayerKind::ShaderMask { .. }
| LayerKind::BackdropFilter { .. }
| LayerKind::AnnotatedRegion { .. }
| LayerKind::Leader { .. } => (world_transform, clip),
LayerKind::Follower(follower) => (
self.follower_transform(world_transform, follower)
.unwrap_or(world_transform),
clip,
),
};
for child in &layer.children {
self.write_debug_at(
*child,
Some(id),
depth + 1,
next_translation,
next_clip,
out,
);
}
}
}
fn brush_generation(brush: &Brush) -> u64 {
match brush {
Brush::Solid(color) => {
u64::from(color.red)
| (u64::from(color.green) << 8)
| (u64::from(color.blue) << 16)
| (u64::from(color.alpha) << 24)
}
Brush::LinearGradient(gradient) => {
gradient.stops.id().get()
^ u64::from(gradient.start.x.to_bits()).rotate_left(7)
^ u64::from(gradient.start.y.to_bits()).rotate_left(13)
^ u64::from(gradient.end.x.to_bits()).rotate_left(19)
^ u64::from(gradient.end.y.to_bits()).rotate_left(23)
^ (gradient.tile_mode as u64).rotate_left(29)
}
Brush::RadialGradient(gradient) => {
gradient.stops.id().get()
^ u64::from(gradient.center.x.to_bits()).rotate_left(7)
^ u64::from(gradient.center.y.to_bits()).rotate_left(13)
^ u64::from(gradient.radius.to_bits()).rotate_left(19)
^ (gradient.tile_mode as u64).rotate_left(29)
^ gradient.focal.map_or(0, |focal| {
u64::from(focal.x.to_bits()).rotate_left(31)
^ u64::from(focal.y.to_bits()).rotate_left(37)
})
^ u64::from(gradient.focal_radius.to_bits()).rotate_left(41)
}
Brush::SweepGradient(gradient) => {
gradient.stops.id().get()
^ u64::from(gradient.center.x.to_bits()).rotate_left(7)
^ u64::from(gradient.center.y.to_bits()).rotate_left(13)
^ u64::from(gradient.start_angle.to_bits()).rotate_left(19)
^ (gradient.tile_mode as u64).rotate_left(29)
}
}
}
fn transform_generation(transform: Transform) -> u64 {
transform
.to_kurbo()
.as_coeffs()
.iter()
.enumerate()
.fold(0, |value, (index, coefficient)| {
value ^ coefficient.to_bits().rotate_left((index as u32 * 9) % 63)
})
}