use api::{BorderRadius, ClipMode, ComplexClipRegion, ImageMask};
use api::{BoxShadowClipMode, ClipId, FillRule, ImageKey, ImageRendering, PipelineId};
use api::units::*;
use crate::image_tiling::{self, Repetition};
use crate::border::{ensure_no_corner_overlap, BorderRadiusAu};
use crate::box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, SpatialNodeIndex, CoordinateSystemId};
use crate::ellipse::Ellipse;
use crate::gpu_cache::GpuCache;
use crate::gpu_types::{BoxShadowStretchMode};
use crate::intern::{self, ItemUid};
use crate::internal_types::{FastHashMap, FastHashSet};
use crate::prim_store::{VisibleMaskImageTile};
use crate::prim_store::{PointKey, SizeKey, RectangleKey, PolygonKey};
use crate::render_task_cache::to_cache_size;
use crate::resource_cache::{ImageRequest, ResourceCache};
use crate::space::SpaceMapper;
use crate::util::{clamp_to_scale_factor, MaxRect, extract_inner_rect_safe, project_rect, ScaleOffset, VecHelper};
use euclid::approxeq::ApproxEq;
use std::{iter, ops, u32, mem};
#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq)]
#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
pub enum ClipIntern {}
pub type ClipDataStore = intern::DataStore<ClipIntern>;
pub type ClipDataHandle = intern::Handle<ClipIntern>;
#[cfg_attr(feature = "capture", derive(Serialize))]
#[derive(Copy, Clone, PartialEq)]
#[derive(MallocSizeOf)]
pub struct ClipInstance {
pub handle: ClipDataHandle,
pub spatial_node_index: SpatialNodeIndex,
}
impl ClipInstance {
pub fn new(
handle: ClipDataHandle,
spatial_node_index: SpatialNodeIndex,
) -> Self {
ClipInstance {
handle,
spatial_node_index,
}
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[derive(MallocSizeOf)]
#[derive(Copy, Clone)]
pub struct SceneClipInstance {
pub clip: ClipInstance,
pub key: ClipItemKey,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ClipTemplate {
pub parent: ClipId,
pub clips: ops::Range<u32>,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ClipChainBuilder {
clip_chain_id: ClipChainId,
parent_clips: FastHashSet<(ItemUid, SpatialNodeIndex)>,
existing_clips_cache: FastHashSet<(ItemUid, SpatialNodeIndex)>,
prev_clip_id: ClipId,
prev_clip_chain_id: ClipChainId,
}
impl ClipChainBuilder {
fn new(
parent_clip_chain_id: ClipChainId,
clip_id: Option<ClipId>,
clip_chain_nodes: &mut Vec<ClipChainNode>,
templates: &FastHashMap<ClipId, ClipTemplate>,
instances: &[SceneClipInstance],
) -> Self {
let mut parent_clips = FastHashSet::default();
let mut current_clip_chain_id = parent_clip_chain_id;
while current_clip_chain_id != ClipChainId::NONE {
let clip_chain_node = &clip_chain_nodes[current_clip_chain_id.0 as usize];
parent_clips.insert((clip_chain_node.handle.uid(), clip_chain_node.spatial_node_index));
current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
}
let clip_chain_id = match clip_id {
Some(clip_id) => {
ClipChainBuilder::add_new_clips_to_chain(
clip_id,
parent_clip_chain_id,
&mut parent_clips,
clip_chain_nodes,
templates,
instances,
)
}
None => {
parent_clip_chain_id
}
};
ClipChainBuilder {
clip_chain_id,
existing_clips_cache: parent_clips.clone(),
parent_clips,
prev_clip_id: ClipId::root(PipelineId::dummy()),
prev_clip_chain_id: ClipChainId::NONE,
}
}
fn add_new_clips_to_chain(
clip_id: ClipId,
parent_clip_chain_id: ClipChainId,
existing_clips: &mut FastHashSet<(ItemUid, SpatialNodeIndex)>,
clip_chain_nodes: &mut Vec<ClipChainNode>,
templates: &FastHashMap<ClipId, ClipTemplate>,
clip_instances: &[SceneClipInstance],
) -> ClipChainId {
let template = &templates[&clip_id];
let instances = &clip_instances[template.clips.start as usize .. template.clips.end as usize];
let mut clip_chain_id = parent_clip_chain_id;
for clip in instances {
let key = (clip.clip.handle.uid(), clip.clip.spatial_node_index);
if existing_clips.contains(&key) {
continue;
}
let new_clip_chain_id = ClipChainId(clip_chain_nodes.len() as u32);
existing_clips.insert(key);
clip_chain_nodes.push(ClipChainNode {
handle: clip.clip.handle,
spatial_node_index: clip.clip.spatial_node_index,
parent_clip_chain_id: clip_chain_id,
});
clip_chain_id = new_clip_chain_id;
}
if clip_id == template.parent {
return clip_chain_id;
}
ClipChainBuilder::add_new_clips_to_chain(
template.parent,
clip_chain_id,
existing_clips,
clip_chain_nodes,
templates,
clip_instances,
)
}
fn has_complex_clips(
&self,
clip_id: ClipId,
templates: &FastHashMap<ClipId, ClipTemplate>,
instances: &[SceneClipInstance],
) -> bool {
let template = &templates[&clip_id];
let clips = &instances[template.clips.start as usize .. template.clips.end as usize];
for clip in clips {
if let ClipNodeKind::Complex = clip.key.kind.node_kind() {
return true;
}
}
if clip_id == template.parent {
return false;
}
self.has_complex_clips(
template.parent,
templates,
instances,
)
}
fn get_or_build_clip_chain_id(
&mut self,
clip_id: ClipId,
clip_chain_nodes: &mut Vec<ClipChainNode>,
templates: &FastHashMap<ClipId, ClipTemplate>,
instances: &[SceneClipInstance],
) -> ClipChainId {
if self.prev_clip_id == clip_id {
return self.prev_clip_chain_id;
}
self.existing_clips_cache.clear();
self.existing_clips_cache.reserve(self.parent_clips.len());
for clip in &self.parent_clips {
self.existing_clips_cache.insert(*clip);
}
let clip_chain_id = ClipChainBuilder::add_new_clips_to_chain(
clip_id,
self.clip_chain_id,
&mut self.existing_clips_cache,
clip_chain_nodes,
templates,
instances,
);
self.prev_clip_id = clip_id;
self.prev_clip_chain_id = clip_chain_id;
clip_chain_id
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone, MallocSizeOf)]
pub enum ClipNodeKind {
Rectangle,
Complex,
}
#[derive(Debug)]
enum ClipResult {
Accept,
Reject,
Partial,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(MallocSizeOf)]
pub struct ClipNode {
pub item: ClipItem,
}
impl From<ClipItemKey> for ClipNode {
fn from(item: ClipItemKey) -> Self {
let kind = match item.kind {
ClipItemKeyKind::Rectangle(rect, mode) => {
ClipItemKind::Rectangle { rect: rect.into(), mode }
}
ClipItemKeyKind::RoundedRectangle(rect, radius, mode) => {
ClipItemKind::RoundedRectangle {
rect: rect.into(),
radius: radius.into(),
mode,
}
}
ClipItemKeyKind::ImageMask(rect, image, repeat, polygon_handle) => {
ClipItemKind::Image {
image,
rect: rect.into(),
repeat,
polygon_handle,
}
}
ClipItemKeyKind::BoxShadow(shadow_rect_fract_offset, shadow_rect_size, shadow_radius, prim_shadow_rect, blur_radius, clip_mode) => {
ClipItemKind::new_box_shadow(
shadow_rect_fract_offset.into(),
shadow_rect_size.into(),
shadow_radius.into(),
prim_shadow_rect.into(),
blur_radius.to_f32_px(),
clip_mode,
)
}
};
ClipNode {
item: ClipItem {
kind,
},
}
}
}
bitflags! {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(MallocSizeOf)]
pub struct ClipNodeFlags: u8 {
const SAME_SPATIAL_NODE = 0x1;
const SAME_COORD_SYSTEM = 0x2;
const USE_FAST_PATH = 0x4;
}
}
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ClipChainId(pub u32);
impl ClipChainId {
pub const NONE: Self = ClipChainId(u32::MAX);
pub const INVALID: Self = ClipChainId(0xDEADBEEF);
}
#[derive(Clone, Debug, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ClipChainNode {
pub handle: ClipDataHandle,
pub spatial_node_index: SpatialNodeIndex,
pub parent_clip_chain_id: ClipChainId,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ClipSet {
pub local_clip_rect: LayoutRect,
pub clip_chain_id: ClipChainId,
}
#[derive(Debug, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipNodeInstance {
pub handle: ClipDataHandle,
pub spatial_node_index: SpatialNodeIndex,
pub flags: ClipNodeFlags,
pub visible_tiles: Option<ops::Range<usize>>,
}
impl ClipNodeInstance {
pub fn has_visible_tiles(&self) -> bool {
self.visible_tiles.is_some()
}
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipNodeRange {
pub first: u32,
pub count: u32,
}
impl ClipNodeRange {
pub fn to_range(&self) -> ops::Range<usize> {
let start = self.first as usize;
let end = start + self.count as usize;
ops::Range {
start,
end,
}
}
}
#[derive(Debug, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
enum ClipSpaceConversion {
Local,
ScaleOffset(ScaleOffset),
Transform(LayoutToWorldTransform),
}
impl ClipSpaceConversion {
fn new(
prim_spatial_node_index: SpatialNodeIndex,
clip_spatial_node_index: SpatialNodeIndex,
spatial_tree: &SpatialTree,
) -> Self {
let clip_spatial_node = &spatial_tree
.spatial_nodes[clip_spatial_node_index.0 as usize];
let prim_spatial_node = &spatial_tree
.spatial_nodes[prim_spatial_node_index.0 as usize];
if prim_spatial_node_index == clip_spatial_node_index {
ClipSpaceConversion::Local
} else if prim_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id {
let scale_offset = prim_spatial_node.content_transform
.inverse()
.accumulate(&clip_spatial_node.content_transform);
ClipSpaceConversion::ScaleOffset(scale_offset)
} else {
ClipSpaceConversion::Transform(
spatial_tree
.get_world_transform(clip_spatial_node_index)
.into_transform()
)
}
}
fn to_flags(&self) -> ClipNodeFlags {
match *self {
ClipSpaceConversion::Local => {
ClipNodeFlags::SAME_SPATIAL_NODE | ClipNodeFlags::SAME_COORD_SYSTEM
}
ClipSpaceConversion::ScaleOffset(..) => {
ClipNodeFlags::SAME_COORD_SYSTEM
}
ClipSpaceConversion::Transform(..) => {
ClipNodeFlags::empty()
}
}
}
}
#[derive(MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
struct ClipNodeInfo {
conversion: ClipSpaceConversion,
handle: ClipDataHandle,
spatial_node_index: SpatialNodeIndex,
}
impl ClipNodeInfo {
fn create_instance(
&self,
node: &ClipNode,
clipped_rect: &LayoutRect,
gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache,
mask_tiles: &mut Vec<VisibleMaskImageTile>,
spatial_tree: &SpatialTree,
request_resources: bool,
) -> Option<ClipNodeInstance> {
let mut flags = self.conversion.to_flags();
let is_raster_2d =
flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) ||
spatial_tree
.get_world_viewport_transform(self.spatial_node_index)
.is_2d_axis_aligned();
if is_raster_2d && node.item.kind.supports_fast_path_rendering() {
flags |= ClipNodeFlags::USE_FAST_PATH;
}
let mut visible_tiles = None;
if let ClipItemKind::Image { rect, image, repeat, .. } = node.item.kind {
let request = ImageRequest {
key: image,
rendering: ImageRendering::Auto,
tile: None,
};
if let Some(props) = resource_cache.get_image_properties(image) {
if let Some(tile_size) = props.tiling {
let tile_range_start = mask_tiles.len();
let visible_rect = if repeat {
*clipped_rect
} else {
clipped_rect.intersection(&rect).unwrap_or(*clipped_rect)
};
let repetitions = image_tiling::repetitions(
&rect,
&visible_rect,
rect.size(),
);
for Repetition { origin, .. } in repetitions {
let layout_image_rect = LayoutRect::from_origin_and_size(
origin,
rect.size(),
);
let tiles = image_tiling::tiles(
&layout_image_rect,
&visible_rect,
&props.visible_rect,
tile_size as i32,
);
for tile in tiles {
if request_resources {
resource_cache.request_image(
request.with_tile(tile.offset),
gpu_cache,
);
}
mask_tiles.push(VisibleMaskImageTile {
tile_offset: tile.offset,
tile_rect: tile.rect,
});
}
}
visible_tiles = Some(tile_range_start..mask_tiles.len());
} else if request_resources {
resource_cache.request_image(request, gpu_cache);
}
} else {
warn!("Clip mask with missing image key {:?}", request.key);
return None;
}
}
Some(ClipNodeInstance {
handle: self.handle,
flags,
visible_tiles,
spatial_node_index: self.spatial_node_index,
})
}
}
impl ClipNode {
pub fn update(
&mut self,
device_pixel_scale: DevicePixelScale,
) {
match self.item.kind {
ClipItemKind::Image { .. } |
ClipItemKind::Rectangle { .. } |
ClipItemKind::RoundedRectangle { .. } => {}
ClipItemKind::BoxShadow { ref mut source } => {
let blur_radius_dp = source.blur_radius * 0.5;
let mut content_scale = LayoutToWorldScale::new(1.0) * device_pixel_scale;
content_scale.0 = clamp_to_scale_factor(content_scale.0, false);
let cache_size = to_cache_size(source.shadow_rect_alloc_size, &mut content_scale);
let bs_cache_key = BoxShadowCacheKey {
blur_radius_dp: (blur_radius_dp * content_scale.0).round() as i32,
clip_mode: source.clip_mode,
original_alloc_size: (source.original_alloc_size * content_scale).round().to_i32(),
br_top_left: (source.shadow_radius.top_left * content_scale).round().to_i32(),
br_top_right: (source.shadow_radius.top_right * content_scale).round().to_i32(),
br_bottom_right: (source.shadow_radius.bottom_right * content_scale).round().to_i32(),
br_bottom_left: (source.shadow_radius.bottom_left * content_scale).round().to_i32(),
device_pixel_scale: Au::from_f32_px(content_scale.0),
};
source.cache_key = Some((cache_size, bs_cache_key));
}
}
}
}
pub struct ClipStoreStats {
templates_capacity: usize,
instances_capacity: usize,
}
impl ClipStoreStats {
pub fn empty() -> Self {
ClipStoreStats {
templates_capacity: 0,
instances_capacity: 0,
}
}
}
#[derive(Default)]
pub struct ClipStoreScratchBuffer {
clip_node_instances: Vec<ClipNodeInstance>,
mask_tiles: Vec<VisibleMaskImageTile>,
}
#[derive(MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ClipStore {
pub clip_chain_nodes: Vec<ClipChainNode>,
pub clip_node_instances: Vec<ClipNodeInstance>,
mask_tiles: Vec<VisibleMaskImageTile>,
active_clip_node_info: Vec<ClipNodeInfo>,
active_local_clip_rect: Option<LayoutRect>,
active_pic_clip_rect: PictureRect,
#[ignore_malloc_size_of = "range missing"]
pub templates: FastHashMap<ClipId, ClipTemplate>,
pub instances: Vec<SceneClipInstance>,
#[ignore_malloc_size_of = "range missing"]
chain_builder_stack: Vec<ClipChainBuilder>,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ClipChainInstance {
pub clips_range: ClipNodeRange,
pub local_clip_rect: LayoutRect,
pub has_non_local_clips: bool,
pub needs_mask: bool,
pub pic_clip_rect: PictureRect,
pub pic_spatial_node_index: SpatialNodeIndex,
}
impl ClipChainInstance {
pub fn empty() -> Self {
ClipChainInstance {
clips_range: ClipNodeRange {
first: 0,
count: 0,
},
local_clip_rect: LayoutRect::zero(),
has_non_local_clips: false,
needs_mask: false,
pic_clip_rect: PictureRect::zero(),
pic_spatial_node_index: ROOT_SPATIAL_NODE_INDEX,
}
}
}
pub struct ClipChainLevel {
shared_clips: Vec<ClipInstance>,
first_clip_index: usize,
initial_clip_counts_len: usize,
}
pub struct ClipChainStack {
levels: Vec<ClipChainLevel>,
clips: Vec<ClipChainId>,
clip_counts: Vec<usize>,
}
impl ClipChainStack {
pub fn new() -> Self {
ClipChainStack {
levels: vec![
ClipChainLevel {
shared_clips: Vec::new(),
first_clip_index: 0,
initial_clip_counts_len: 0,
}
],
clips: Vec::new(),
clip_counts: Vec::new(),
}
}
pub fn clear(&mut self) {
self.clips.clear();
self.clip_counts.clear();
self.levels.clear();
self.levels.push(ClipChainLevel {
shared_clips: Vec::new(),
first_clip_index: 0,
initial_clip_counts_len: 0,
});
}
pub fn take(&mut self) -> Self {
ClipChainStack {
levels: self.levels.take(),
clips: self.clips.take(),
clip_counts: self.clip_counts.take(),
}
}
pub fn push_clip(
&mut self,
clip_chain_id: ClipChainId,
clip_store: &ClipStore,
) {
let mut clip_count = 0;
let mut current_clip_chain_id = clip_chain_id;
while current_clip_chain_id != ClipChainId::NONE {
let clip_chain_node = &clip_store.clip_chain_nodes[current_clip_chain_id.0 as usize];
let clip_uid = clip_chain_node.handle.uid();
let mut valid_clip = true;
for level in &self.levels {
if level.shared_clips.iter().any(|instance| {
instance.handle.uid() == clip_uid &&
instance.spatial_node_index == clip_chain_node.spatial_node_index
}) {
valid_clip = false;
break;
}
}
if valid_clip {
self.clips.push(current_clip_chain_id);
clip_count += 1;
}
current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
}
self.clip_counts.push(clip_count);
}
pub fn pop_clip(&mut self) {
let count = self.clip_counts.pop().unwrap();
for _ in 0 .. count {
self.clips.pop().unwrap();
}
}
pub fn push_surface(
&mut self,
maybe_shared_clips: &[ClipInstance],
spatial_tree: &SpatialTree,
) {
let mut shared_clips = Vec::with_capacity(maybe_shared_clips.len());
for clip in maybe_shared_clips {
let spatial_node = &spatial_tree.spatial_nodes[clip.spatial_node_index.0 as usize];
if spatial_node.coordinate_system_id == CoordinateSystemId::root() {
shared_clips.push(*clip);
}
}
let level = ClipChainLevel {
shared_clips,
first_clip_index: self.clips.len(),
initial_clip_counts_len: self.clip_counts.len(),
};
self.levels.push(level);
}
pub fn pop_surface(&mut self) {
let level = self.levels.pop().unwrap();
assert!(self.clip_counts.len() == level.initial_clip_counts_len);
assert!(self.clips.len() == level.first_clip_index);
}
pub fn current_clips_array(&self) -> &[ClipChainId] {
let first = self.levels.last().unwrap().first_clip_index;
&self.clips[first..]
}
}
impl ClipStore {
pub fn new(stats: &ClipStoreStats) -> Self {
let mut templates = FastHashMap::default();
templates.reserve(stats.templates_capacity);
ClipStore {
clip_chain_nodes: Vec::new(),
clip_node_instances: Vec::new(),
mask_tiles: Vec::new(),
active_clip_node_info: Vec::new(),
active_local_clip_rect: None,
active_pic_clip_rect: PictureRect::max_rect(),
templates,
instances: Vec::with_capacity(stats.instances_capacity),
chain_builder_stack: Vec::new(),
}
}
pub fn get_stats(&self) -> ClipStoreStats {
let templates_capacity = self.templates.capacity().min(self.templates.len() * 2);
let instances_capacity = self.instances.capacity().min(self.instances.len() * 2);
ClipStoreStats {
templates_capacity,
instances_capacity,
}
}
pub fn register_clip_template(
&mut self,
clip_id: ClipId,
parent: ClipId,
clips: &[SceneClipInstance],
) {
let start = self.instances.len() as u32;
self.instances.extend_from_slice(clips);
let end = self.instances.len() as u32;
self.templates.insert(clip_id, ClipTemplate {
parent,
clips: start..end,
});
}
pub fn get_template(
&self,
clip_id: ClipId,
) -> &ClipTemplate {
&self.templates[&clip_id]
}
pub fn get_or_build_clip_chain_id(
&mut self,
clip_id: ClipId,
) -> ClipChainId {
self.chain_builder_stack
.last_mut()
.unwrap()
.get_or_build_clip_chain_id(
clip_id,
&mut self.clip_chain_nodes,
&self.templates,
&self.instances,
)
}
pub fn has_complex_clips(
&self,
clip_id: ClipId,
) -> bool {
self.chain_builder_stack
.last()
.unwrap()
.has_complex_clips(
clip_id,
&self.templates,
&self.instances,
)
}
pub fn push_clip_root(
&mut self,
clip_id: Option<ClipId>,
link_to_parent: bool,
) {
let parent_clip_chain_id = if link_to_parent {
self.chain_builder_stack.last().unwrap().clip_chain_id
} else {
ClipChainId::NONE
};
let builder = ClipChainBuilder::new(
parent_clip_chain_id,
clip_id,
&mut self.clip_chain_nodes,
&self.templates,
&self.instances,
);
self.chain_builder_stack.push(builder);
}
pub fn pop_clip_root(
&mut self,
) {
self.chain_builder_stack.pop().unwrap();
}
pub fn get_clip_chain(&self, clip_chain_id: ClipChainId) -> &ClipChainNode {
&self.clip_chain_nodes[clip_chain_id.0 as usize]
}
pub fn add_clip_chain_node(
&mut self,
handle: ClipDataHandle,
spatial_node_index: SpatialNodeIndex,
parent_clip_chain_id: ClipChainId,
) -> ClipChainId {
let id = ClipChainId(self.clip_chain_nodes.len() as u32);
self.clip_chain_nodes.push(ClipChainNode {
handle,
spatial_node_index,
parent_clip_chain_id,
});
id
}
pub fn get_instance_from_range(
&self,
node_range: &ClipNodeRange,
index: u32,
) -> &ClipNodeInstance {
&self.clip_node_instances[(node_range.first + index) as usize]
}
pub fn set_active_clips(
&mut self,
local_prim_clip_rect: LayoutRect,
prim_spatial_node_index: SpatialNodeIndex,
pic_spatial_node_index: SpatialNodeIndex,
clip_chains: &[ClipChainId],
spatial_tree: &SpatialTree,
clip_data_store: &ClipDataStore,
) {
self.active_clip_node_info.clear();
self.active_local_clip_rect = None;
self.active_pic_clip_rect = PictureRect::max_rect();
let mut local_clip_rect = local_prim_clip_rect;
for clip_chain_id in clip_chains {
let clip_chain_node = &self.clip_chain_nodes[clip_chain_id.0 as usize];
if !add_clip_node_to_current_chain(
clip_chain_node,
prim_spatial_node_index,
pic_spatial_node_index,
&mut local_clip_rect,
&mut self.active_clip_node_info,
&mut self.active_pic_clip_rect,
clip_data_store,
spatial_tree,
) {
return;
}
}
self.active_local_clip_rect = Some(local_clip_rect);
}
pub fn set_active_clips_from_clip_chain(
&mut self,
prim_clip_chain: &ClipChainInstance,
prim_spatial_node_index: SpatialNodeIndex,
spatial_tree: &SpatialTree,
) {
self.active_clip_node_info.clear();
self.active_local_clip_rect = Some(prim_clip_chain.local_clip_rect);
self.active_pic_clip_rect = prim_clip_chain.pic_clip_rect;
let clip_instances = &self
.clip_node_instances[prim_clip_chain.clips_range.to_range()];
for clip_instance in clip_instances {
let conversion = ClipSpaceConversion::new(
prim_spatial_node_index,
clip_instance.spatial_node_index,
spatial_tree,
);
self.active_clip_node_info.push(ClipNodeInfo {
handle: clip_instance.handle,
spatial_node_index: clip_instance.spatial_node_index,
conversion,
});
}
}
pub fn build_clip_chain_instance(
&mut self,
local_prim_rect: LayoutRect,
prim_to_pic_mapper: &SpaceMapper<LayoutPixel, PicturePixel>,
pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
spatial_tree: &SpatialTree,
gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache,
device_pixel_scale: DevicePixelScale,
world_rect: &WorldRect,
clip_data_store: &mut ClipDataStore,
request_resources: bool,
is_chased: bool,
) -> Option<ClipChainInstance> {
let local_clip_rect = match self.active_local_clip_rect {
Some(rect) => rect,
None => return None,
};
profile_scope!("build_clip_chain_instance");
if is_chased {
println!("\tbuilding clip chain instance with local rect {:?}", local_prim_rect);
}
let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?;
let mut pic_clip_rect = prim_to_pic_mapper.map(&local_bounding_rect)?;
let world_clip_rect = pic_to_world_mapper.map(&pic_clip_rect)?;
let first_clip_node_index = self.clip_node_instances.len() as u32;
let mut has_non_local_clips = false;
let mut needs_mask = false;
for node_info in self.active_clip_node_info.drain(..) {
let node = &mut clip_data_store[node_info.handle];
let clip_result = match node_info.conversion {
ClipSpaceConversion::Local => {
node.item.kind.get_clip_result(&local_bounding_rect)
}
ClipSpaceConversion::ScaleOffset(ref scale_offset) => {
has_non_local_clips = true;
node.item.kind.get_clip_result(&scale_offset.unmap_rect(&local_bounding_rect))
}
ClipSpaceConversion::Transform(ref transform) => {
has_non_local_clips = true;
node.item.kind.get_clip_result_complex(
transform,
&world_clip_rect,
world_rect,
)
}
};
if is_chased {
println!("\t\tclip {:?}", node.item);
println!("\t\tflags {:?}, resulted in {:?}", node_info.conversion.to_flags(), clip_result);
}
match clip_result {
ClipResult::Accept => {
}
ClipResult::Reject => {
return None;
}
ClipResult::Partial => {
node.update(device_pixel_scale);
if let Some(instance) = node_info.create_instance(
node,
&local_bounding_rect,
gpu_cache,
resource_cache,
&mut self.mask_tiles,
spatial_tree,
request_resources,
) {
needs_mask |= match node.item.kind {
ClipItemKind::Rectangle { mode: ClipMode::ClipOut, .. } |
ClipItemKind::RoundedRectangle { .. } |
ClipItemKind::Image { .. } |
ClipItemKind::BoxShadow { .. } => {
true
}
ClipItemKind::Rectangle { mode: ClipMode::Clip, .. } => {
!instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM)
}
};
self.clip_node_instances.push(instance);
}
}
}
}
let clips_range = ClipNodeRange {
first: first_clip_node_index,
count: self.clip_node_instances.len() as u32 - first_clip_node_index,
};
if needs_mask {
pic_clip_rect = pic_clip_rect.intersection(&self.active_pic_clip_rect)?;
}
Some(ClipChainInstance {
clips_range,
has_non_local_clips,
local_clip_rect: local_clip_rect,
pic_clip_rect: pic_clip_rect,
pic_spatial_node_index: prim_to_pic_mapper.ref_spatial_node_index,
needs_mask,
})
}
pub fn begin_frame(&mut self, scratch: &mut ClipStoreScratchBuffer) {
mem::swap(&mut self.clip_node_instances, &mut scratch.clip_node_instances);
mem::swap(&mut self.mask_tiles, &mut scratch.mask_tiles);
self.clip_node_instances.clear();
self.mask_tiles.clear();
}
pub fn end_frame(&mut self, scratch: &mut ClipStoreScratchBuffer) {
mem::swap(&mut self.clip_node_instances, &mut scratch.clip_node_instances);
mem::swap(&mut self.mask_tiles, &mut scratch.mask_tiles);
}
pub fn visible_mask_tiles(&self, instance: &ClipNodeInstance) -> &[VisibleMaskImageTile] {
if let Some(range) = &instance.visible_tiles {
&self.mask_tiles[range.clone()]
} else {
&[]
}
}
}
pub struct ComplexTranslateIter<I> {
source: I,
offset: LayoutVector2D,
}
impl<I: Iterator<Item = ComplexClipRegion>> Iterator for ComplexTranslateIter<I> {
type Item = ComplexClipRegion;
fn next(&mut self) -> Option<Self::Item> {
self.source
.next()
.map(|mut complex| {
complex.rect = complex.rect.translate(self.offset);
complex
})
}
}
#[derive(Copy, Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ClipItemKeyKind {
Rectangle(RectangleKey, ClipMode),
RoundedRectangle(RectangleKey, BorderRadiusAu, ClipMode),
ImageMask(RectangleKey, ImageKey, bool, Option<PolygonDataHandle>),
BoxShadow(PointKey, SizeKey, BorderRadiusAu, RectangleKey, Au, BoxShadowClipMode),
}
impl ClipItemKeyKind {
pub fn rectangle(rect: LayoutRect, mode: ClipMode) -> Self {
ClipItemKeyKind::Rectangle(rect.into(), mode)
}
pub fn rounded_rect(rect: LayoutRect, mut radii: BorderRadius, mode: ClipMode) -> Self {
if radii.is_zero() {
ClipItemKeyKind::rectangle(rect, mode)
} else {
ensure_no_corner_overlap(&mut radii, rect.size());
ClipItemKeyKind::RoundedRectangle(
rect.into(),
radii.into(),
mode,
)
}
}
pub fn image_mask(image_mask: &ImageMask, mask_rect: LayoutRect,
polygon_handle: Option<PolygonDataHandle>) -> Self {
ClipItemKeyKind::ImageMask(
mask_rect.into(),
image_mask.image,
image_mask.repeat,
polygon_handle,
)
}
pub fn box_shadow(
shadow_rect: LayoutRect,
shadow_radius: BorderRadius,
prim_shadow_rect: LayoutRect,
blur_radius: f32,
clip_mode: BoxShadowClipMode,
) -> Self {
let fract_offset = LayoutPoint::new(
shadow_rect.min.x.fract().abs(),
shadow_rect.min.y.fract().abs(),
);
ClipItemKeyKind::BoxShadow(
fract_offset.into(),
shadow_rect.size().into(),
shadow_radius.into(),
prim_shadow_rect.into(),
Au::from_f32_px(blur_radius),
clip_mode,
)
}
pub fn node_kind(&self) -> ClipNodeKind {
match *self {
ClipItemKeyKind::Rectangle(_, ClipMode::Clip) => ClipNodeKind::Rectangle,
ClipItemKeyKind::Rectangle(_, ClipMode::ClipOut) |
ClipItemKeyKind::RoundedRectangle(..) |
ClipItemKeyKind::ImageMask(..) |
ClipItemKeyKind::BoxShadow(..) => ClipNodeKind::Complex,
}
}
}
#[derive(Debug, Copy, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipItemKey {
pub kind: ClipItemKeyKind,
}
#[derive(Debug, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipInternData {
pub clip_node_kind: ClipNodeKind,
}
impl intern::InternDebug for ClipItemKey {}
impl intern::Internable for ClipIntern {
type Key = ClipItemKey;
type StoreData = ClipNode;
type InternData = ClipInternData;
const PROFILE_COUNTER: usize = crate::profiler::INTERNED_CLIPS;
}
#[derive(Debug, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ClipItemKind {
Rectangle {
rect: LayoutRect,
mode: ClipMode,
},
RoundedRectangle {
rect: LayoutRect,
radius: BorderRadius,
mode: ClipMode,
},
Image {
image: ImageKey,
rect: LayoutRect,
repeat: bool,
polygon_handle: Option<PolygonDataHandle>,
},
BoxShadow {
source: BoxShadowClipSource,
},
}
#[derive(Debug, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipItem {
pub kind: ClipItemKind,
}
fn compute_box_shadow_parameters(
shadow_rect_fract_offset: LayoutPoint,
shadow_rect_size: LayoutSize,
mut shadow_radius: BorderRadius,
prim_shadow_rect: LayoutRect,
blur_radius: f32,
clip_mode: BoxShadowClipMode,
) -> BoxShadowClipSource {
ensure_no_corner_overlap(&mut shadow_radius, shadow_rect_size);
let fract_size = LayoutSize::new(
shadow_rect_size.width.fract().abs(),
shadow_rect_size.height.fract().abs(),
);
let max_corner_width = shadow_radius.top_left.width
.max(shadow_radius.bottom_left.width)
.max(shadow_radius.top_right.width)
.max(shadow_radius.bottom_right.width);
let max_corner_height = shadow_radius.top_left.height
.max(shadow_radius.bottom_left.height)
.max(shadow_radius.top_right.height)
.max(shadow_radius.bottom_right.height);
let blur_region = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
let used_corner_width = max_corner_width.max(blur_region);
let used_corner_height = max_corner_height.max(blur_region);
let min_shadow_rect_size = LayoutSize::new(
2.0 * used_corner_width + blur_region,
2.0 * used_corner_height + blur_region,
);
let mut minimal_shadow_rect = LayoutRect::from_origin_and_size(
LayoutPoint::new(
blur_region + shadow_rect_fract_offset.x,
blur_region + shadow_rect_fract_offset.y,
),
LayoutSize::new(
min_shadow_rect_size.width + fract_size.width,
min_shadow_rect_size.height + fract_size.height,
),
);
let mut stretch_mode_x = BoxShadowStretchMode::Stretch;
if shadow_rect_size.width < minimal_shadow_rect.width() {
minimal_shadow_rect.max.x = minimal_shadow_rect.min.x + shadow_rect_size.width;
stretch_mode_x = BoxShadowStretchMode::Simple;
}
let mut stretch_mode_y = BoxShadowStretchMode::Stretch;
if shadow_rect_size.height < minimal_shadow_rect.height() {
minimal_shadow_rect.max.y = minimal_shadow_rect.min.y + shadow_rect_size.height;
stretch_mode_y = BoxShadowStretchMode::Simple;
}
let shadow_rect_alloc_size = LayoutSize::new(
2.0 * blur_region + minimal_shadow_rect.width().ceil(),
2.0 * blur_region + minimal_shadow_rect.height().ceil(),
);
BoxShadowClipSource {
original_alloc_size: shadow_rect_alloc_size,
shadow_rect_alloc_size,
shadow_radius,
prim_shadow_rect,
blur_radius,
clip_mode,
stretch_mode_x,
stretch_mode_y,
render_task: None,
cache_key: None,
minimal_shadow_rect,
}
}
impl ClipItemKind {
pub fn new_box_shadow(
shadow_rect_fract_offset: LayoutPoint,
shadow_rect_size: LayoutSize,
mut shadow_radius: BorderRadius,
prim_shadow_rect: LayoutRect,
blur_radius: f32,
clip_mode: BoxShadowClipMode,
) -> Self {
let mut source = compute_box_shadow_parameters(
shadow_rect_fract_offset,
shadow_rect_size,
shadow_radius,
prim_shadow_rect,
blur_radius,
clip_mode,
);
fn needed_downscaling(source: &BoxShadowClipSource) -> Option<f32> {
const MAX_SIZE: f32 = 2048.;
let max_dimension =
source.shadow_rect_alloc_size.width.max(source.shadow_rect_alloc_size.height);
if max_dimension > MAX_SIZE {
Some(MAX_SIZE / max_dimension)
} else {
None
}
}
if let Some(downscale) = needed_downscaling(&source) {
shadow_radius.bottom_left.height *= downscale;
shadow_radius.bottom_left.width *= downscale;
shadow_radius.bottom_right.height *= downscale;
shadow_radius.bottom_right.width *= downscale;
shadow_radius.top_left.height *= downscale;
shadow_radius.top_left.width *= downscale;
shadow_radius.top_right.height *= downscale;
shadow_radius.top_right.width *= downscale;
let original_alloc_size = source.shadow_rect_alloc_size;
source = compute_box_shadow_parameters(
shadow_rect_fract_offset * downscale,
shadow_rect_size * downscale,
shadow_radius,
prim_shadow_rect,
blur_radius * downscale,
clip_mode,
);
source.original_alloc_size = original_alloc_size;
}
ClipItemKind::BoxShadow { source }
}
fn supports_fast_path_rendering(&self) -> bool {
match *self {
ClipItemKind::Rectangle { .. } |
ClipItemKind::Image { .. } |
ClipItemKind::BoxShadow { .. } => {
false
}
ClipItemKind::RoundedRectangle { ref radius, .. } => {
radius.is_uniform().is_some()
}
}
}
pub fn get_local_clip_rect(&self) -> Option<LayoutRect> {
match *self {
ClipItemKind::Rectangle { rect, mode: ClipMode::Clip } => Some(rect),
ClipItemKind::Rectangle { mode: ClipMode::ClipOut, .. } => None,
ClipItemKind::RoundedRectangle { rect, mode: ClipMode::Clip, .. } => Some(rect),
ClipItemKind::RoundedRectangle { mode: ClipMode::ClipOut, .. } => None,
ClipItemKind::Image { repeat, rect, .. } => {
if repeat {
None
} else {
Some(rect)
}
}
ClipItemKind::BoxShadow { .. } => None,
}
}
fn get_clip_result_complex(
&self,
transform: &LayoutToWorldTransform,
prim_world_rect: &WorldRect,
world_rect: &WorldRect,
) -> ClipResult {
let visible_rect = match prim_world_rect.intersection(world_rect) {
Some(rect) => rect,
None => return ClipResult::Reject,
};
let (clip_rect, inner_rect, mode) = match *self {
ClipItemKind::Rectangle { rect, mode } => {
(rect, Some(rect), mode)
}
ClipItemKind::RoundedRectangle { rect, ref radius, mode } => {
let inner_clip_rect = extract_inner_rect_safe(&rect, radius);
(rect, inner_clip_rect, mode)
}
ClipItemKind::Image { rect, repeat: false, .. } => {
(rect, None, ClipMode::Clip)
}
ClipItemKind::Image { repeat: true, .. } |
ClipItemKind::BoxShadow { .. } => {
return ClipResult::Partial;
}
};
if let Some(ref inner_clip_rect) = inner_rect {
if let Some(()) = projected_rect_contains(inner_clip_rect, transform, &visible_rect) {
return match mode {
ClipMode::Clip => ClipResult::Accept,
ClipMode::ClipOut => ClipResult::Reject,
};
}
}
match mode {
ClipMode::Clip => {
let outer_clip_rect = match project_rect(
transform,
&clip_rect,
&world_rect,
) {
Some(outer_clip_rect) => outer_clip_rect,
None => return ClipResult::Partial,
};
match outer_clip_rect.intersection(prim_world_rect) {
Some(..) => {
ClipResult::Partial
}
None => {
ClipResult::Reject
}
}
}
ClipMode::ClipOut => ClipResult::Partial,
}
}
fn get_clip_result(
&self,
prim_rect: &LayoutRect,
) -> ClipResult {
match *self {
ClipItemKind::Rectangle { rect, mode: ClipMode::Clip } => {
if rect.contains_box(prim_rect) {
return ClipResult::Accept;
}
match rect.intersection(prim_rect) {
Some(..) => {
ClipResult::Partial
}
None => {
ClipResult::Reject
}
}
}
ClipItemKind::Rectangle { rect, mode: ClipMode::ClipOut } => {
if rect.contains_box(prim_rect) {
return ClipResult::Reject;
}
match rect.intersection(prim_rect) {
Some(_) => {
ClipResult::Partial
}
None => {
ClipResult::Accept
}
}
}
ClipItemKind::RoundedRectangle { rect, ref radius, mode: ClipMode::Clip } => {
if rounded_rectangle_contains_box_quick(&rect, radius, &prim_rect) {
return ClipResult::Accept;
}
match rect.intersection(prim_rect) {
Some(..) => {
ClipResult::Partial
}
None => {
ClipResult::Reject
}
}
}
ClipItemKind::RoundedRectangle { rect, ref radius, mode: ClipMode::ClipOut } => {
if rounded_rectangle_contains_box_quick(&rect, radius, &prim_rect) {
return ClipResult::Reject;
}
match rect.intersection(prim_rect) {
Some(_) => {
ClipResult::Partial
}
None => {
ClipResult::Accept
}
}
}
ClipItemKind::Image { rect, repeat, .. } => {
if repeat {
ClipResult::Partial
} else {
match rect.intersection(prim_rect) {
Some(..) => {
ClipResult::Partial
}
None => {
ClipResult::Reject
}
}
}
}
ClipItemKind::BoxShadow { .. } => {
ClipResult::Partial
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Geometry {
pub local_rect: LayoutRect,
pub device_rect: DeviceIntRect,
}
impl From<LayoutRect> for Geometry {
fn from(local_rect: LayoutRect) -> Self {
Geometry {
local_rect,
device_rect: DeviceIntRect::zero(),
}
}
}
pub fn rounded_rectangle_contains_point(
point: &LayoutPoint,
rect: &LayoutRect,
radii: &BorderRadius
) -> bool {
if !rect.contains(*point) {
return false;
}
let top_left_center = rect.min + radii.top_left.to_vector();
if top_left_center.x > point.x && top_left_center.y > point.y &&
!Ellipse::new(radii.top_left).contains(*point - top_left_center.to_vector()) {
return false;
}
let bottom_right_center = rect.bottom_right() - radii.bottom_right.to_vector();
if bottom_right_center.x < point.x && bottom_right_center.y < point.y &&
!Ellipse::new(radii.bottom_right).contains(*point - bottom_right_center.to_vector()) {
return false;
}
let top_right_center = rect.top_right() +
LayoutVector2D::new(-radii.top_right.width, radii.top_right.height);
if top_right_center.x < point.x && top_right_center.y > point.y &&
!Ellipse::new(radii.top_right).contains(*point - top_right_center.to_vector()) {
return false;
}
let bottom_left_center = rect.bottom_left() +
LayoutVector2D::new(radii.bottom_left.width, -radii.bottom_left.height);
if bottom_left_center.x > point.x && bottom_left_center.y < point.y &&
!Ellipse::new(radii.bottom_left).contains(*point - bottom_left_center.to_vector()) {
return false;
}
true
}
fn rounded_rectangle_contains_box_quick(
container: &LayoutRect,
radii: &BorderRadius,
containee: &LayoutRect,
) -> bool {
if !container.contains_box(containee) {
return false;
}
fn foul(point: LayoutPoint, corner: LayoutPoint) -> bool {
point.x < corner.x && point.y < corner.y
}
fn flip_x(pt: LayoutPoint) -> LayoutPoint {
LayoutPoint { x: -pt.x, .. pt }
}
fn flip_y(pt: LayoutPoint) -> LayoutPoint {
LayoutPoint { y: -pt.y, .. pt }
}
if foul(containee.top_left(), container.top_left() + radii.top_left) ||
foul(flip_x(containee.top_right()), flip_x(container.top_right()) + radii.top_right) ||
foul(flip_y(containee.bottom_left()), flip_y(container.bottom_left()) + radii.bottom_left) ||
foul(-containee.bottom_right(), -container.bottom_right() + radii.bottom_right)
{
return false;
}
true
}
pub fn is_left_of_line(
p_x: f32,
p_y: f32,
p0_x: f32,
p0_y: f32,
p1_x: f32,
p1_y: f32,
) -> f32 {
(p1_x - p0_x) * (p_y - p0_y) - (p_x - p0_x) * (p1_y - p0_y)
}
pub fn polygon_contains_point(
point: &LayoutPoint,
rect: &LayoutRect,
polygon: &PolygonKey,
) -> bool {
if !rect.contains(*point) {
return false;
}
let p = LayoutPoint::new(point.x - rect.min.x, point.y - rect.min.y);
let mut winding_number: i32 = 0;
let count = polygon.point_count as usize;
for i in 0..count {
let p0 = polygon.points[i];
let p1 = polygon.points[(i + 1) % count];
if p0.y <= p.y {
if p1.y > p.y {
if is_left_of_line(p.x, p.y, p0.x, p0.y, p1.x, p1.y) > 0.0 {
winding_number = winding_number + 1;
}
}
} else if p1.y <= p.y {
if is_left_of_line(p.x, p.y, p0.x, p0.y, p1.x, p1.y) < 0.0 {
winding_number = winding_number - 1;
}
}
}
match polygon.fill_rule {
FillRule::Nonzero => winding_number != 0,
FillRule::Evenodd => winding_number.abs() % 2 == 1,
}
}
pub fn projected_rect_contains(
source_rect: &LayoutRect,
transform: &LayoutToWorldTransform,
target_rect: &WorldRect,
) -> Option<()> {
let points = [
transform.transform_point2d(source_rect.top_left())?,
transform.transform_point2d(source_rect.top_right())?,
transform.transform_point2d(source_rect.bottom_right())?,
transform.transform_point2d(source_rect.bottom_left())?,
];
let target_points = [
target_rect.top_left(),
target_rect.top_right(),
target_rect.bottom_right(),
target_rect.bottom_left(),
];
for (a, b) in points
.iter()
.cloned()
.zip(points[1..].iter().cloned().chain(iter::once(points[0])))
{
if a.approx_eq(&b) || target_points.iter().any(|&c| (b - a).cross(c - a) < 0.0) {
return None
}
}
Some(())
}
fn add_clip_node_to_current_chain(
node: &ClipChainNode,
prim_spatial_node_index: SpatialNodeIndex,
pic_spatial_node_index: SpatialNodeIndex,
local_clip_rect: &mut LayoutRect,
clip_node_info: &mut Vec<ClipNodeInfo>,
current_pic_clip_rect: &mut PictureRect,
clip_data_store: &ClipDataStore,
spatial_tree: &SpatialTree,
) -> bool {
let clip_node = &clip_data_store[node.handle];
let conversion = ClipSpaceConversion::new(
prim_spatial_node_index,
node.spatial_node_index,
spatial_tree,
);
if let Some(clip_rect) = clip_node.item.kind.get_local_clip_rect() {
match conversion {
ClipSpaceConversion::Local => {
*local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
Some(rect) => rect,
None => return false,
};
}
ClipSpaceConversion::ScaleOffset(ref scale_offset) => {
let clip_rect = scale_offset.map_rect(&clip_rect);
*local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
Some(rect) => rect,
None => return false,
};
}
ClipSpaceConversion::Transform(..) => {
let pic_coord_system = spatial_tree
.spatial_nodes[pic_spatial_node_index.0 as usize]
.coordinate_system_id;
let clip_coord_system = spatial_tree
.spatial_nodes[node.spatial_node_index.0 as usize]
.coordinate_system_id;
if pic_coord_system == clip_coord_system {
let mapper = SpaceMapper::new_with_target(
pic_spatial_node_index,
node.spatial_node_index,
PictureRect::max_rect(),
spatial_tree,
);
if let Some(pic_clip_rect) = mapper.map(&clip_rect) {
*current_pic_clip_rect = pic_clip_rect
.intersection(current_pic_clip_rect)
.unwrap_or(PictureRect::zero());
}
}
}
}
}
clip_node_info.push(ClipNodeInfo {
conversion,
spatial_node_index: node.spatial_node_index,
handle: node.handle,
});
true
}
#[cfg(test)]
mod tests {
use super::projected_rect_contains;
use euclid::{Transform3D, rect};
#[test]
fn test_empty_projected_rect() {
assert_eq!(
None,
projected_rect_contains(
&rect(10.0, 10.0, 0.0, 0.0).to_box2d(),
&Transform3D::identity(),
&rect(20.0, 20.0, 10.0, 10.0).to_box2d(),
),
"Empty rectangle is considered to include a non-empty!"
);
}
}
#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
pub enum PolygonIntern {}
pub type PolygonDataHandle = intern::Handle<PolygonIntern>;
impl intern::InternDebug for PolygonKey {}
impl intern::Internable for PolygonIntern {
type Key = PolygonKey;
type StoreData = PolygonKey;
type InternData = PolygonKey;
const PROFILE_COUNTER: usize = crate::profiler::INTERNED_POLYGONS;
}