1use api::{BorderRadius, ClipMode, ImageMask, ClipId, ClipChainId};
96use api::{FillRule, ImageKey, ImageRendering};
97use api::units::*;
98use crate::image_tiling::{self, Repetition};
99use crate::border::{ensure_no_corner_overlap, BorderRadiusAu};
100use crate::renderer::GpuBufferBuilderF;
101use crate::spatial_tree::{SceneSpatialTree, SpatialTree, SpatialNodeIndex};
102use crate::ellipse::Ellipse;
103use crate::intern;
104use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo};
105use crate::prim_store::{VisibleMaskImageTile};
106use crate::prim_store::{RectKey, PolygonKey};
107use crate::render_task::RenderTask;
108use crate::render_task_graph::RenderTaskGraphBuilder;
109use crate::resource_cache::{ImageRequest, ResourceCache};
110use crate::scene_builder_thread::Interners;
111use crate::space::SpaceMapper;
112use crate::util::{extract_inner_rect_safe, project_rect, MatrixHelpers, MaxRect, ScaleOffset};
113use euclid::approxeq::ApproxEq;
114use std::{iter, ops, u32, mem};
115
116#[cfg_attr(feature = "capture", derive(Serialize))]
118#[cfg_attr(feature = "replay", derive(Deserialize))]
119#[derive(MallocSizeOf)]
120pub struct ClipTreeNode {
121 pub handle: ClipDataHandle,
122 pub spatial_node_index: SpatialNodeIndex,
123 pub unsnapped_clip_rect: LayoutRect,
126 pub snapped_clip_rect: LayoutRect,
130 pub parent: ClipNodeId,
131
132 children: FastHashMap<ClipEntry, ClipNodeId>,
133
134 }
137
138#[cfg_attr(feature = "capture", derive(Serialize))]
141#[cfg_attr(feature = "replay", derive(Deserialize))]
142#[derive(MallocSizeOf)]
143pub struct ClipTreeLeaf {
144 pub node_id: ClipNodeId,
145
146 pub unsnapped_local_clip_rect: LayoutRect,
153 pub snapped_local_clip_rect: LayoutRect,
159}
160
161#[derive(Copy, Clone, PartialEq, MallocSizeOf, Eq, Hash)]
163#[cfg_attr(feature = "capture", derive(Serialize))]
164#[cfg_attr(feature = "replay", derive(Deserialize))]
165pub struct ClipNodeId(u32);
166
167impl ClipNodeId {
168 pub const NONE: ClipNodeId = ClipNodeId(0);
169}
170
171impl std::fmt::Debug for ClipNodeId {
172 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
173 if *self == Self::NONE {
174 write!(f, "<none>")
175 } else {
176 write!(f, "#{}", self.0)
177 }
178 }
179}
180
181#[derive(Copy, Clone, PartialEq, MallocSizeOf, Eq, Hash)]
183#[cfg_attr(feature = "capture", derive(Serialize))]
184#[cfg_attr(feature = "replay", derive(Deserialize))]
185pub struct ClipLeafId(u32);
186
187impl std::fmt::Debug for ClipLeafId {
188 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
189 write!(f, "#{}", self.0)
190 }
191}
192
193#[cfg_attr(feature = "capture", derive(Serialize))]
195#[cfg_attr(feature = "replay", derive(Deserialize))]
196pub struct ClipTree {
197 nodes: Vec<ClipTreeNode>,
198 leaves: Vec<ClipTreeLeaf>,
199 clip_root_stack: Vec<ClipNodeId>,
200}
201
202impl ClipTree {
203 pub fn new() -> Self {
204 ClipTree {
205 nodes: vec![
206 ClipTreeNode {
207 handle: ClipDataHandle::INVALID,
208 spatial_node_index: SpatialNodeIndex::INVALID,
209 unsnapped_clip_rect: LayoutRect::zero(),
210 snapped_clip_rect: LayoutRect::zero(),
211 children: FastHashMap::default(),
212 parent: ClipNodeId::NONE,
213 }
214 ],
215 leaves: Vec::new(),
216 clip_root_stack: vec![
217 ClipNodeId::NONE,
218 ],
219 }
220 }
221
222 pub fn reset(&mut self) {
223 self.nodes.clear();
224 self.nodes.push(ClipTreeNode {
225 handle: ClipDataHandle::INVALID,
226 spatial_node_index: SpatialNodeIndex::INVALID,
227 unsnapped_clip_rect: LayoutRect::zero(),
228 snapped_clip_rect: LayoutRect::zero(),
229 children: FastHashMap::default(),
230 parent: ClipNodeId::NONE,
231 });
232
233 self.leaves.clear();
234
235 self.clip_root_stack.clear();
236 self.clip_root_stack.push(ClipNodeId::NONE);
237 }
238
239 fn add_impl(
242 mut id: ClipNodeId,
243 clips: &[ClipEntry],
244 nodes: &mut Vec<ClipTreeNode>,
245 ) -> ClipNodeId {
246 if clips.is_empty() {
247 return id;
248 }
249
250 for clip in clips {
251 let key = *clip; let node_index = nodes[id.0 as usize]
254 .children
255 .get(&key)
256 .cloned();
257
258 let node_index = match node_index {
259 Some(node_index) => node_index,
260 None => {
261 let node_index = ClipNodeId(nodes.len() as u32);
262 nodes[id.0 as usize].children.insert(key, node_index);
263 nodes.push(ClipTreeNode {
264 handle: key.handle,
265 spatial_node_index: key.spatial_node_index,
266 unsnapped_clip_rect: key.clip_rect.into(),
267 snapped_clip_rect: LayoutRect::zero(),
268 children: FastHashMap::default(),
269 parent: id,
270 });
271 node_index
272 }
273 };
274 id = node_index;
275 }
276 id
277 }
278
279 pub fn add(
282 &mut self,
283 root: ClipNodeId,
284 clips: &[ClipEntry],
285 ) -> ClipNodeId {
286 ClipTree::add_impl(
287 root,
288 clips,
289 &mut self.nodes,
290 )
291 }
292
293 pub fn current_clip_root(&self) -> ClipNodeId {
296 self.clip_root_stack.last().cloned().unwrap()
297 }
298
299 pub fn push_clip_root_leaf(&mut self, clip_leaf_id: ClipLeafId) {
302 let leaf = &self.leaves[clip_leaf_id.0 as usize];
303 self.clip_root_stack.push(leaf.node_id);
304 }
305
306 pub fn push_clip_root_node(&mut self, clip_node_id: ClipNodeId) {
309 self.clip_root_stack.push(clip_node_id);
310 }
311
312 pub fn pop_clip_root(&mut self) {
314 self.clip_root_stack.pop().unwrap();
315 }
316
317 pub fn get_node(&self, id: ClipNodeId) -> &ClipTreeNode {
319 assert!(id != ClipNodeId::NONE);
320
321 &self.nodes[id.0 as usize]
322 }
323
324 pub fn get_parent(&self, id: ClipNodeId) -> Option<ClipNodeId> {
325 let parent = self.nodes[id.0 as usize].parent;
329 if parent == ClipNodeId::NONE {
330 return None;
331 }
332
333 return Some(parent)
334 }
335
336 pub fn get_leaf(&self, id: ClipLeafId) -> &ClipTreeLeaf {
338 &self.leaves[id.0 as usize]
339 }
340
341 pub fn nodes_mut(&mut self) -> &mut [ClipTreeNode] {
344 &mut self.nodes
345 }
346
347 pub fn get_leaf_mut(&mut self, id: ClipLeafId) -> &mut ClipTreeLeaf {
351 &mut self.leaves[id.0 as usize]
352 }
353
354 #[allow(unused)]
356 pub fn print(&self) {
357 use crate::print_tree::PrintTree;
358
359 fn print_node<T: crate::print_tree::PrintTreePrinter>(
360 id: ClipNodeId,
361 nodes: &[ClipTreeNode],
362 pt: &mut T,
363 ) {
364 let node = &nodes[id.0 as usize];
365
366 pt.new_level(format!("{:?}", id));
367 pt.add_item(format!("{:?}", node.handle));
368
369 for child_id in node.children.values() {
370 print_node(*child_id, nodes, pt);
371 }
372
373 pt.end_level();
374 }
375
376 fn print_leaf<T: crate::print_tree::PrintTreePrinter>(
377 id: ClipLeafId,
378 leaves: &[ClipTreeLeaf],
379 pt: &mut T,
380 ) {
381 let leaf = &leaves[id.0 as usize];
382
383 pt.new_level(format!("{:?}", id));
384 pt.add_item(format!("node_id: {:?}", leaf.node_id));
385 pt.add_item(format!("unsnapped_local_clip_rect: {:?}", leaf.unsnapped_local_clip_rect));
386 pt.end_level();
387 }
388
389 let mut pt = PrintTree::new("clip tree");
390 print_node(ClipNodeId::NONE, &self.nodes, &mut pt);
391
392 for i in 0 .. self.leaves.len() {
393 print_leaf(ClipLeafId(i as u32), &self.leaves, &mut pt);
394 }
395 }
396
397 pub fn find_lowest_common_ancestor(
400 &self,
401 mut node1: ClipNodeId,
402 mut node2: ClipNodeId,
403 ) -> ClipNodeId {
404 fn get_node_depth(
406 id: ClipNodeId,
407 nodes: &[ClipTreeNode],
408 ) -> usize {
409 let mut depth = 0;
410 let mut current = id;
411
412 while current != ClipNodeId::NONE {
413 let node = &nodes[current.0 as usize];
414 depth += 1;
415 current = node.parent;
416 }
417
418 depth
419 }
420
421 let mut depth1 = get_node_depth(node1, &self.nodes);
422 let mut depth2 = get_node_depth(node2, &self.nodes);
423
424 while depth1 > depth2 {
425 node1 = self.nodes[node1.0 as usize].parent;
426 depth1 -= 1;
427 }
428
429 while depth2 > depth1 {
430 node2 = self.nodes[node2.0 as usize].parent;
431 depth2 -= 1;
432 }
433
434 while node1 != node2 {
435 node1 = self.nodes[node1.0 as usize].parent;
436 node2 = self.nodes[node2.0 as usize].parent;
437 }
438
439 node1
440 }
441}
442
443#[derive(Copy, Clone, PartialEq, Eq, Hash, MallocSizeOf)]
445#[cfg_attr(feature = "capture", derive(Serialize))]
446#[cfg_attr(feature = "replay", derive(Deserialize))]
447pub struct ClipEntry {
448 pub handle: ClipDataHandle,
449 pub spatial_node_index: SpatialNodeIndex,
450 pub clip_rect: RectKey,
451}
452
453#[cfg_attr(feature = "capture", derive(Serialize))]
457#[cfg_attr(feature = "replay", derive(Deserialize))]
458pub struct ClipChain {
459 parent: Option<usize>,
460 clips: Vec<ClipEntry>,
461}
462
463#[cfg_attr(feature = "capture", derive(Serialize))]
464#[cfg_attr(feature = "replay", derive(Deserialize))]
465pub struct ClipStackEntry {
466 last_clip_chain_cache: Option<(ClipChainId, ClipNodeId)>,
468
469 seen_clips: FastHashSet<ClipEntry>,
471
472 clip_node_id: ClipNodeId,
474}
475
476#[cfg_attr(feature = "capture", derive(Serialize))]
478#[cfg_attr(feature = "replay", derive(Deserialize))]
479pub struct ClipTreeBuilder {
480 clip_map: FastHashMap<ClipId, ClipEntry>,
482
483 clip_chains: Vec<ClipChain>,
485 clip_chain_map: FastHashMap<ClipChainId, usize>,
486
487 clip_stack: Vec<ClipStackEntry>,
489
490 tree: ClipTree,
492
493 clip_handles_buffer: Vec<ClipEntry>,
495}
496
497impl ClipTreeBuilder {
498 pub fn new() -> Self {
499 ClipTreeBuilder {
500 clip_map: FastHashMap::default(),
501 clip_chain_map: FastHashMap::default(),
502 clip_chains: Vec::new(),
503 clip_stack: vec![
504 ClipStackEntry {
505 clip_node_id: ClipNodeId::NONE,
506 last_clip_chain_cache: None,
507 seen_clips: FastHashSet::default(),
508 },
509 ],
510 tree: ClipTree::new(),
511 clip_handles_buffer: Vec::new(),
512 }
513 }
514
515 pub fn begin(&mut self) {
516 self.clip_map.clear();
517 self.clip_chain_map.clear();
518 self.clip_chains.clear();
519 self.clip_stack.clear();
520 self.clip_stack.push(ClipStackEntry {
521 clip_node_id: ClipNodeId::NONE,
522 last_clip_chain_cache: None,
523 seen_clips: FastHashSet::default(),
524 });
525 self.tree.reset();
526 self.clip_handles_buffer.clear();
527 }
528
529 pub fn recycle_tree(&mut self, tree: ClipTree) {
530 self.tree = tree;
531 }
532
533 pub fn define_rect_clip(
535 &mut self,
536 id: ClipId,
537 handle: ClipDataHandle,
538 spatial_node_index: SpatialNodeIndex,
539 clip_rect: LayoutRect,
540 ) {
541 self.clip_map.insert(id, ClipEntry { handle, spatial_node_index, clip_rect: clip_rect.into() });
542 }
543
544 pub fn define_rounded_rect_clip(
546 &mut self,
547 id: ClipId,
548 handle: ClipDataHandle,
549 spatial_node_index: SpatialNodeIndex,
550 clip_rect: LayoutRect,
551 ) {
552 self.clip_map.insert(id, ClipEntry { handle, spatial_node_index, clip_rect: clip_rect.into() });
553 }
554
555 pub fn define_image_mask_clip(
557 &mut self,
558 id: ClipId,
559 handle: ClipDataHandle,
560 spatial_node_index: SpatialNodeIndex,
561 clip_rect: LayoutRect,
562 ) {
563 self.clip_map.insert(id, ClipEntry { handle, spatial_node_index, clip_rect: clip_rect.into() });
564 }
565
566 pub fn define_clip_chain<I: Iterator<Item = ClipId>>(
568 &mut self,
569 id: ClipChainId,
570 parent: Option<ClipChainId>,
571 clips: I,
572 ) {
573 let parent = parent.map(|ref id| self.clip_chain_map[id]);
574 let index = self.clip_chains.len();
575 let clips = clips.map(|clip_id| {
576 self.clip_map[&clip_id]
577 }).collect();
578 self.clip_chains.push(ClipChain {
579 parent,
580 clips,
581 });
582 self.clip_chain_map.insert(id, index);
583 }
584
585 pub fn push_clip_chain(
587 &mut self,
588 clip_chain_id: Option<ClipChainId>,
589 reset_seen: bool,
590 ignore_ancestor_clips: bool,
591 ) {
592 let (mut clip_node_id, mut seen_clips) = {
593 let prev = self.clip_stack.last().unwrap();
594 let clip_node_id = if ignore_ancestor_clips {
595 ClipNodeId::NONE
596 } else {
597 prev.clip_node_id
598 };
599 (clip_node_id, prev.seen_clips.clone())
600 };
601
602 if let Some(clip_chain_id) = clip_chain_id {
603 if clip_chain_id != ClipChainId::INVALID {
604 self.clip_handles_buffer.clear();
605
606 let clip_chain_index = self.clip_chain_map[&clip_chain_id];
607 ClipTreeBuilder::add_clips(
608 clip_chain_index,
609 &mut seen_clips,
610 &mut self.clip_handles_buffer,
611 &self.clip_chains,
612 );
613
614 clip_node_id = self.tree.add(
615 clip_node_id,
616 &self.clip_handles_buffer,
617 );
618 }
619 }
620
621 if reset_seen {
622 seen_clips.clear();
623 }
624
625 self.clip_stack.push(ClipStackEntry {
626 last_clip_chain_cache: None,
627 clip_node_id,
628 seen_clips,
629 });
630 }
631
632 pub fn push_clip_id(
634 &mut self,
635 clip_id: ClipId,
636 ) {
637 let (clip_node_id, mut seen_clips) = {
638 let prev = self.clip_stack.last().unwrap();
639 (prev.clip_node_id, prev.seen_clips.clone())
640 };
641
642 self.clip_handles_buffer.clear();
643 let clip_entry = self.clip_map[&clip_id];
644
645 if seen_clips.insert(clip_entry) {
646 self.clip_handles_buffer.push(clip_entry);
647 }
648
649 let clip_node_id = self.tree.add(
650 clip_node_id,
651 &self.clip_handles_buffer,
652 );
653
654 self.clip_stack.push(ClipStackEntry {
655 last_clip_chain_cache: None,
656 seen_clips,
657 clip_node_id,
658 });
659 }
660
661 pub fn pop_clip(&mut self) {
663 self.clip_stack.pop().unwrap();
664 }
665
666 fn add_clips(
668 clip_chain_index: usize,
669 seen_clips: &mut FastHashSet<ClipEntry>,
670 output: &mut Vec<ClipEntry>,
671 clip_chains: &[ClipChain],
672 ) {
673 let clip_chain = &clip_chains[clip_chain_index];
681
682 if let Some(parent) = clip_chain.parent {
683 ClipTreeBuilder::add_clips(
684 parent,
685 seen_clips,
686 output,
687 clip_chains,
688 );
689 }
690
691 for clip_entry in clip_chain.clips.iter().rev() {
692 if seen_clips.insert(*clip_entry) {
693 output.push(*clip_entry);
694 }
695 }
696 }
697
698 pub fn build_clip_set(
700 &mut self,
701 clip_chain_id: ClipChainId,
702 ) -> ClipNodeId {
703 let clip_stack = self.clip_stack.last_mut().unwrap();
704
705 if clip_chain_id == ClipChainId::INVALID {
706 clip_stack.clip_node_id
707 } else {
708 if let Some((cached_clip_chain, cached_clip_node)) = clip_stack.last_clip_chain_cache {
709 if cached_clip_chain == clip_chain_id {
710 return cached_clip_node;
711 }
712 }
713
714 let clip_chain_index = self.clip_chain_map[&clip_chain_id];
715
716 self.clip_handles_buffer.clear();
717
718 ClipTreeBuilder::add_clips(
719 clip_chain_index,
720 &mut clip_stack.seen_clips,
721 &mut self.clip_handles_buffer,
722 &self.clip_chains,
723 );
724
725 for entry in &self.clip_handles_buffer {
731 clip_stack.seen_clips.remove(entry);
732 }
733
734 let clip_node_id = self.tree.add(
735 clip_stack.clip_node_id,
736 &self.clip_handles_buffer,
737 );
738
739 clip_stack.last_clip_chain_cache = Some((clip_chain_id, clip_node_id));
740
741 clip_node_id
742 }
743 }
744
745 fn has_complex_clips_impl(
747 &self,
748 clip_chain_index: usize,
749 interners: &Interners,
750 ) -> bool {
751 let clip_chain = &self.clip_chains[clip_chain_index];
752
753 for clip_entry in &clip_chain.clips {
754 let clip_info = &interners.clip[clip_entry.handle];
755
756 if let ClipNodeKind::Complex = clip_info.key.kind.node_kind() {
757 return true;
758 }
759 }
760
761 match clip_chain.parent {
762 Some(parent) => self.has_complex_clips_impl(parent, interners),
763 None => false,
764 }
765 }
766
767 pub fn clip_chain_has_complex_clips(
769 &self,
770 clip_chain_id: ClipChainId,
771 interners: &Interners,
772 ) -> bool {
773 let clip_chain_index = self.clip_chain_map[&clip_chain_id];
774 self.has_complex_clips_impl(clip_chain_index, interners)
775 }
776
777 pub fn clip_chain_complex_clips_are_promotable(
782 &self,
783 clip_chain_id: ClipChainId,
784 interners: &Interners,
785 spatial_tree: &SceneSpatialTree,
786 ) -> bool {
787 let clip_chain_index = self.clip_chain_map[&clip_chain_id];
788 self.complex_clips_are_promotable_impl(clip_chain_index, interners, spatial_tree)
789 }
790
791 fn complex_clips_are_promotable_impl(
792 &self,
793 clip_chain_index: usize,
794 interners: &Interners,
795 spatial_tree: &SceneSpatialTree,
796 ) -> bool {
797 let mut index = clip_chain_index;
798
799 loop {
800 let clip_chain = &self.clip_chains[index];
801
802 for clip_entry in &clip_chain.clips {
803 let clip_info = &interners.clip[clip_entry.handle];
804
805 match clip_info.key.kind {
806 ClipItemKeyKind::Rectangle(ClipMode::Clip) => {}
807 ClipItemKeyKind::RoundedRectangle(_, ClipMode::Clip) => {
808 if !spatial_tree.is_root_coord_system(clip_entry.spatial_node_index) {
809 return false;
810 }
811 }
812 _ => return false,
813 }
814 }
815
816 match clip_chain.parent {
817 Some(parent) => index = parent,
818 None => return true,
819 }
820 }
821 }
822
823 pub fn clip_node_has_complex_clips(
825 &self,
826 clip_node_id: ClipNodeId,
827 interners: &Interners,
828 ) -> bool {
829 let mut current = clip_node_id;
830
831 while current != ClipNodeId::NONE {
832 let node = &self.tree.nodes[current.0 as usize];
833 let clip_info = &interners.clip[node.handle];
834
835 if let ClipNodeKind::Complex = clip_info.key.kind.node_kind() {
836 return true;
837 }
838
839 current = node.parent;
840 }
841
842 false
843 }
844
845 pub fn get_parent(&self, id: ClipNodeId) -> Option<ClipNodeId> {
846 self.tree.get_parent(id)
847 }
848
849 pub fn finalize(&mut self) -> ClipTree {
851 std::mem::replace(&mut self.tree, ClipTree {
855 nodes: Vec::new(),
856 leaves: Vec::new(),
857 clip_root_stack: Vec::new(),
858 })
859 }
860
861 pub fn get_node(&self, id: ClipNodeId) -> &ClipTreeNode {
863 assert!(id != ClipNodeId::NONE);
864
865 &self.tree.nodes[id.0 as usize]
866 }
867
868 pub fn get_leaf(&self, id: ClipLeafId) -> &ClipTreeLeaf {
870 &self.tree.leaves[id.0 as usize]
871 }
872
873 pub fn build_for_tile_cache(
875 &mut self,
876 clip_node_id: ClipNodeId,
877 extra_clips: &[ClipId],
878 ) -> ClipLeafId {
879 self.clip_handles_buffer.clear();
880
881 for clip_id in extra_clips {
882 let entry = self.clip_map[clip_id];
883 self.clip_handles_buffer.push(entry);
884 }
885
886 let node_id = self.tree.add(
887 clip_node_id,
888 &self.clip_handles_buffer,
889 );
890
891 let clip_leaf_id = ClipLeafId(self.tree.leaves.len() as u32);
892
893 self.tree.leaves.push(ClipTreeLeaf {
894 node_id,
895 unsnapped_local_clip_rect: LayoutRect::max_rect(),
896 snapped_local_clip_rect: LayoutRect::max_rect(),
897 });
898
899 clip_leaf_id
900 }
901
902 pub fn build_for_picture(
904 &mut self,
905 clip_node_id: ClipNodeId,
906 ) -> ClipLeafId {
907 let node_id = self.tree.add(
908 clip_node_id,
909 &[],
910 );
911
912 let clip_leaf_id = ClipLeafId(self.tree.leaves.len() as u32);
913
914 self.tree.leaves.push(ClipTreeLeaf {
915 node_id,
916 unsnapped_local_clip_rect: LayoutRect::max_rect(),
917 snapped_local_clip_rect: LayoutRect::max_rect(),
918 });
919
920 clip_leaf_id
921 }
922
923 pub fn build_for_prim(
925 &mut self,
926 clip_node_id: ClipNodeId,
927 info: &LayoutPrimitiveInfo,
928 extra_clips: &[ClipItemEntry],
929 interners: &mut Interners,
930 ) -> ClipLeafId {
931
932 let node_id = if extra_clips.is_empty() {
933 clip_node_id
934 } else {
935 self.clip_handles_buffer.clear();
938
939 for clip_item_entry in extra_clips {
940 let handle = interners.clip.intern(&clip_item_entry.key, || {
943 ClipInternData {
944 key: clip_item_entry.key.clone(),
945 }
946 });
947
948 self.clip_handles_buffer.push(ClipEntry {
949 handle,
950 spatial_node_index: clip_item_entry.spatial_node_index,
951 clip_rect: clip_item_entry.clip_rect.into(),
952 });
953 }
954
955 self.tree.add(
956 clip_node_id,
957 &self.clip_handles_buffer,
958 )
959 };
960
961 let clip_leaf_id = ClipLeafId(self.tree.leaves.len() as u32);
962
963 self.tree.leaves.push(ClipTreeLeaf {
964 node_id,
965 unsnapped_local_clip_rect: info.clip_rect,
966 snapped_local_clip_rect: LayoutRect::zero(),
967 });
968
969 clip_leaf_id
970 }
971
972 pub fn find_lowest_common_ancestor(
974 &self,
975 node1: ClipNodeId,
976 node2: ClipNodeId,
977 ) -> ClipNodeId {
978 self.tree.find_lowest_common_ancestor(node1, node2)
979 }
980}
981
982#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, Eq, Hash)]
985#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
986pub enum ClipIntern {}
987
988pub type ClipDataStore = intern::DataStore<ClipIntern>;
989pub type ClipDataHandle = intern::Handle<ClipIntern>;
990
991#[cfg_attr(feature = "capture", derive(Serialize))]
994#[cfg_attr(feature = "replay", derive(Deserialize))]
995#[derive(Debug, Copy, Clone, MallocSizeOf)]
996pub enum ClipNodeKind {
997 Rectangle,
999 Complex,
1001}
1002
1003#[derive(Debug)]
1005enum ClipResult {
1006 Accept,
1008 Reject,
1010 Partial,
1013}
1014
1015#[derive(Debug)]
1020#[cfg_attr(feature = "capture", derive(Serialize))]
1021#[cfg_attr(feature = "replay", derive(Deserialize))]
1022#[derive(MallocSizeOf)]
1023pub struct ClipNode {
1024 pub item: ClipItem,
1025}
1026
1027impl From<ClipItemKey> for ClipNode {
1030 fn from(item: ClipItemKey) -> Self {
1031 let kind = match item.kind {
1032 ClipItemKeyKind::Rectangle(mode) => {
1033 ClipItemKind::Rectangle { mode }
1034 }
1035 ClipItemKeyKind::RoundedRectangle(radius, mode) => {
1036 ClipItemKind::RoundedRectangle {
1037 radius: radius.into(),
1038 mode,
1039 }
1040 }
1041 ClipItemKeyKind::ImageMask(image, polygon_handle) => {
1042 ClipItemKind::Image {
1043 image,
1044 polygon_handle,
1045 }
1046 }
1047 };
1048
1049 ClipNode {
1050 item: ClipItem {
1051 kind,
1052 },
1053 }
1054 }
1055}
1056
1057#[cfg_attr(feature = "capture", derive(Serialize))]
1059#[cfg_attr(feature = "replay", derive(Deserialize))]
1060#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, MallocSizeOf)]
1061pub struct ClipNodeFlags(u8);
1062
1063bitflags! {
1064 impl ClipNodeFlags : u8 {
1065 const SAME_SPATIAL_NODE = 0x1;
1066 const SAME_COORD_SYSTEM = 0x2;
1067 const USE_FAST_PATH = 0x4;
1068 }
1069}
1070
1071impl core::fmt::Debug for ClipNodeFlags {
1072 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1073 if self.is_empty() {
1074 write!(f, "{:#x}", Self::empty().bits())
1075 } else {
1076 bitflags::parser::to_writer(self, f)
1077 }
1078 }
1079}
1080
1081#[derive(Debug, Clone, MallocSizeOf)]
1088#[cfg_attr(feature = "capture", derive(Serialize))]
1089#[cfg_attr(feature = "replay", derive(Deserialize))]
1090pub struct ClipNodeInstance {
1091 pub handle: ClipDataHandle,
1092 pub spatial_node_index: SpatialNodeIndex,
1093 pub clip_rect: LayoutRect,
1094 pub flags: ClipNodeFlags,
1095 pub visible_tiles: Option<ops::Range<usize>>,
1096}
1097
1098impl ClipNodeInstance {
1099 pub fn has_visible_tiles(&self) -> bool {
1100 self.visible_tiles.is_some()
1101 }
1102}
1103
1104#[derive(Debug, Copy, Clone)]
1107#[cfg_attr(feature = "capture", derive(Serialize))]
1108#[cfg_attr(feature = "replay", derive(Deserialize))]
1109pub struct ClipNodeRange {
1110 pub first: u32,
1111 pub count: u32,
1112}
1113
1114impl ClipNodeRange {
1115 pub fn to_range(&self) -> ops::Range<usize> {
1116 let start = self.first as usize;
1117 let end = start + self.count as usize;
1118
1119 ops::Range {
1120 start,
1121 end,
1122 }
1123 }
1124}
1125
1126#[derive(Debug, MallocSizeOf)]
1138#[cfg_attr(feature = "capture", derive(Serialize))]
1139pub enum ClipSpaceConversion {
1140 Local,
1142 ScaleOffset(ScaleOffset),
1147 Transform(LayoutToVisTransform),
1152}
1153
1154impl ClipSpaceConversion {
1155 pub fn new(
1157 prim_spatial_node_index: SpatialNodeIndex,
1158 clip_spatial_node_index: SpatialNodeIndex,
1159 visibility_spatial_node_index: SpatialNodeIndex,
1160 spatial_tree: &SpatialTree,
1161 ) -> Self {
1162 let clip_spatial_node = spatial_tree.get_spatial_node(clip_spatial_node_index);
1166 let prim_spatial_node = spatial_tree.get_spatial_node(prim_spatial_node_index);
1167
1168 if prim_spatial_node_index == clip_spatial_node_index {
1169 ClipSpaceConversion::Local
1170 } else if prim_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id {
1171 let scale_offset = clip_spatial_node.content_transform
1172 .then(&prim_spatial_node.content_transform.inverse());
1173 ClipSpaceConversion::ScaleOffset(scale_offset)
1174 } else {
1175 ClipSpaceConversion::Transform(
1176 spatial_tree.get_relative_transform(
1177 clip_spatial_node_index,
1178 visibility_spatial_node_index,
1179 ).into_transform().cast_unit()
1180 )
1181 }
1182 }
1183
1184 fn to_flags(&self) -> ClipNodeFlags {
1185 match *self {
1186 ClipSpaceConversion::Local => {
1187 ClipNodeFlags::SAME_SPATIAL_NODE | ClipNodeFlags::SAME_COORD_SYSTEM
1188 }
1189 ClipSpaceConversion::ScaleOffset(..) => {
1190 ClipNodeFlags::SAME_COORD_SYSTEM
1191 }
1192 ClipSpaceConversion::Transform(..) => {
1193 ClipNodeFlags::empty()
1194 }
1195 }
1196 }
1197}
1198
1199#[derive(MallocSizeOf)]
1202#[cfg_attr(feature = "capture", derive(Serialize))]
1203struct ClipNodeInfo {
1204 conversion: ClipSpaceConversion,
1205 handle: ClipDataHandle,
1206 spatial_node_index: SpatialNodeIndex,
1207 clip_rect: LayoutRect,
1208}
1209
1210impl ClipNodeInfo {
1211 fn create_instance(
1212 &self,
1213 node: &ClipNode,
1214 clipped_rect: &LayoutRect,
1215 gpu_buffer: &mut GpuBufferBuilderF,
1216 resource_cache: &mut ResourceCache,
1217 mask_tiles: &mut Vec<VisibleMaskImageTile>,
1218 spatial_tree: &SpatialTree,
1219 rg_builder: &mut RenderTaskGraphBuilder,
1220 request_resources: bool,
1221 ) -> Option<ClipNodeInstance> {
1222 let mut flags = self.conversion.to_flags();
1225
1226 let is_raster_2d =
1230 flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) ||
1231 spatial_tree
1232 .get_world_viewport_transform(self.spatial_node_index)
1233 .is_2d_axis_aligned();
1234 if is_raster_2d && node.item.kind.supports_fast_path_rendering(self.clip_rect) {
1235 flags |= ClipNodeFlags::USE_FAST_PATH;
1236 }
1237
1238 let mut visible_tiles = None;
1239
1240 if let ClipItemKind::Image { image, .. } = node.item.kind {
1241 let rect = self.clip_rect;
1242 let request = ImageRequest {
1243 key: image,
1244 rendering: ImageRendering::Auto,
1245 tile: None,
1246 };
1247
1248 if let Some(props) = resource_cache.get_image_properties(image) {
1249 if let Some(tile_size) = props.tiling {
1250 let tile_range_start = mask_tiles.len();
1251
1252 let visible_rect =
1257 clipped_rect.intersection(&rect).unwrap_or(*clipped_rect);
1258
1259 let repetitions = image_tiling::repetitions(
1260 &rect,
1261 &visible_rect,
1262 rect.size(),
1263 );
1264
1265 for Repetition { origin, .. } in repetitions {
1266 let layout_image_rect = LayoutRect::from_origin_and_size(
1267 origin,
1268 rect.size(),
1269 );
1270 let tiles = image_tiling::tiles(
1271 &layout_image_rect,
1272 &visible_rect,
1273 &props.visible_rect,
1274 tile_size as i32,
1275 );
1276 for tile in tiles {
1277 let req = request.with_tile(tile.offset);
1278
1279 if request_resources {
1280 resource_cache.request_image(
1281 req,
1282 gpu_buffer,
1283 );
1284 }
1285
1286 let task_id = rg_builder.add().init(
1287 RenderTask::new_image(props.descriptor.size, req, false)
1288 );
1289
1290 mask_tiles.push(VisibleMaskImageTile {
1291 tile_offset: tile.offset,
1292 tile_rect: tile.rect,
1293 task_id,
1294 });
1295 }
1296 }
1297 visible_tiles = Some(tile_range_start..mask_tiles.len());
1298 } else {
1299 if request_resources {
1300 resource_cache.request_image(request, gpu_buffer);
1301 }
1302
1303 let tile_range_start = mask_tiles.len();
1304
1305 let task_id = rg_builder.add().init(
1306 RenderTask::new_image(props.descriptor.size, request, false)
1307 );
1308
1309 mask_tiles.push(VisibleMaskImageTile {
1310 tile_rect: rect,
1311 tile_offset: TileOffset::zero(),
1312 task_id,
1313 });
1314
1315 visible_tiles = Some(tile_range_start .. mask_tiles.len());
1316 }
1317 } else {
1318 warn!("Clip mask with missing image key {:?}", request.key);
1321 return None;
1322 }
1323 }
1324
1325 Some(ClipNodeInstance {
1326 handle: self.handle,
1327 spatial_node_index: self.spatial_node_index,
1328 clip_rect: self.clip_rect,
1329 flags,
1330 visible_tiles,
1331 })
1332 }
1333}
1334
1335#[derive(Default)]
1336pub struct ClipStoreScratchBuffer {
1337 clip_node_instances: Vec<ClipNodeInstance>,
1338 mask_tiles: Vec<VisibleMaskImageTile>,
1339}
1340
1341#[derive(MallocSizeOf)]
1343#[cfg_attr(feature = "capture", derive(Serialize))]
1344pub struct ClipStore {
1345 pub clip_node_instances: Vec<ClipNodeInstance>,
1346 mask_tiles: Vec<VisibleMaskImageTile>,
1347
1348 active_clip_node_info: Vec<ClipNodeInfo>,
1349 active_local_clip_rect: Option<LayoutRect>,
1350 active_pic_coverage_rect: PictureRect,
1351}
1352
1353#[derive(Debug, Copy, Clone)]
1356#[cfg_attr(feature = "capture", derive(Serialize))]
1357pub struct ClipChainInstance {
1358 pub clips_range: ClipNodeRange,
1359 pub local_clip_rect: LayoutRect,
1362 pub has_non_local_clips: bool,
1363 pub needs_mask: bool,
1366 pub pic_coverage_rect: PictureRect,
1369 pub pic_spatial_node_index: SpatialNodeIndex,
1371}
1372
1373impl ClipChainInstance {
1374 pub fn empty() -> Self {
1375 ClipChainInstance {
1376 clips_range: ClipNodeRange {
1377 first: 0,
1378 count: 0,
1379 },
1380 local_clip_rect: LayoutRect::zero(),
1381 has_non_local_clips: false,
1382 needs_mask: false,
1383 pic_coverage_rect: PictureRect::zero(),
1384 pic_spatial_node_index: SpatialNodeIndex::INVALID,
1385 }
1386 }
1387}
1388
1389impl ClipStore {
1390 pub fn new() -> Self {
1391 ClipStore {
1392 clip_node_instances: Vec::new(),
1393 mask_tiles: Vec::new(),
1394 active_clip_node_info: Vec::new(),
1395 active_local_clip_rect: None,
1396 active_pic_coverage_rect: PictureRect::max_rect(),
1397 }
1398 }
1399
1400 pub fn reset(&mut self) {
1401 self.clip_node_instances.clear();
1402 self.mask_tiles.clear();
1403 self.active_clip_node_info.clear();
1404 self.active_local_clip_rect = None;
1405 self.active_pic_coverage_rect = PictureRect::max_rect();
1406 }
1407
1408 pub fn get_instance_from_range(
1409 &self,
1410 node_range: &ClipNodeRange,
1411 index: u32,
1412 ) -> &ClipNodeInstance {
1413 &self.clip_node_instances[(node_range.first + index) as usize]
1414 }
1415
1416 pub fn set_active_clips(
1418 &mut self,
1419 prim_spatial_node_index: SpatialNodeIndex,
1420 pic_spatial_node_index: SpatialNodeIndex,
1421 visibility_spatial_node_index: SpatialNodeIndex,
1422 clip_leaf_id: ClipLeafId,
1423 spatial_tree: &SpatialTree,
1424 clip_data_store: &ClipDataStore,
1425 clip_tree: &ClipTree,
1426 ) {
1427 self.active_clip_node_info.clear();
1428 self.active_local_clip_rect = None;
1429 self.active_pic_coverage_rect = PictureRect::max_rect();
1430
1431 let clip_root = clip_tree.current_clip_root();
1432 let clip_leaf = clip_tree.get_leaf(clip_leaf_id);
1433
1434 let mut local_clip_rect = clip_leaf.snapped_local_clip_rect;
1438 let mut current = clip_leaf.node_id;
1439
1440 while current != clip_root && current != ClipNodeId::NONE {
1441 let node = clip_tree.get_node(current);
1442
1443 if !add_clip_node_to_current_chain(
1444 node.handle,
1445 node.spatial_node_index,
1446 node.snapped_clip_rect,
1447 prim_spatial_node_index,
1448 pic_spatial_node_index,
1449 visibility_spatial_node_index,
1450 &mut local_clip_rect,
1451 &mut self.active_clip_node_info,
1452 &mut self.active_pic_coverage_rect,
1453 clip_data_store,
1454 spatial_tree,
1455 ) {
1456 return;
1457 }
1458
1459 current = node.parent;
1460 }
1461
1462 self.active_local_clip_rect = Some(local_clip_rect);
1463 }
1464
1465 pub fn set_active_clips_from_clip_chain(
1467 &mut self,
1468 prim_clip_chain: &ClipChainInstance,
1469 prim_spatial_node_index: SpatialNodeIndex,
1470 visibility_spatial_node_index: SpatialNodeIndex,
1471 spatial_tree: &SpatialTree,
1472 ) {
1473 self.active_clip_node_info.clear();
1478 self.active_local_clip_rect = Some(prim_clip_chain.local_clip_rect);
1479 self.active_pic_coverage_rect = prim_clip_chain.pic_coverage_rect;
1480
1481 let clip_instances = &self
1482 .clip_node_instances[prim_clip_chain.clips_range.to_range()];
1483 for clip_instance in clip_instances {
1484 let conversion = ClipSpaceConversion::new(
1485 prim_spatial_node_index,
1486 clip_instance.spatial_node_index,
1487 visibility_spatial_node_index,
1488 spatial_tree,
1489 );
1490 self.active_clip_node_info.push(ClipNodeInfo {
1491 handle: clip_instance.handle,
1492 conversion,
1493 spatial_node_index: clip_instance.spatial_node_index,
1494 clip_rect: clip_instance.clip_rect,
1495 });
1496 }
1497 }
1498
1499 pub fn get_inner_rect_for_clip_chain(
1504 &self,
1505 clip_chain: &ClipChainInstance,
1506 clip_data_store: &ClipDataStore,
1507 spatial_tree: &SpatialTree,
1508 ) -> Option<PictureRect> {
1509 let mut inner_rect = clip_chain.pic_coverage_rect;
1510 let clip_instances = &self
1511 .clip_node_instances[clip_chain.clips_range.to_range()];
1512
1513 for clip_instance in clip_instances {
1514 if !clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
1516 return None;
1517 }
1518
1519 let clip_node = &clip_data_store[clip_instance.handle];
1520
1521 match clip_node.item.kind {
1522 ClipItemKind::Rectangle { mode: ClipMode::ClipOut, .. } |
1525 ClipItemKind::Image { .. } |
1526 ClipItemKind::RoundedRectangle { mode: ClipMode::ClipOut, .. } => {
1527 return None;
1528 }
1529 ClipItemKind::Rectangle { mode: ClipMode::Clip, .. } => {}
1532 ClipItemKind::RoundedRectangle { mode: ClipMode::Clip, radius } => {
1533 let radius = clamped_radius(&radius, clip_instance.clip_rect.size());
1535 let local_inner_rect = match extract_inner_rect_safe(&clip_instance.clip_rect, &radius) {
1536 Some(rect) => rect,
1537 None => return None,
1538 };
1539
1540 let mapper = SpaceMapper::new_with_target(
1542 clip_chain.pic_spatial_node_index,
1543 clip_instance.spatial_node_index,
1544 PictureRect::max_rect(),
1545 spatial_tree,
1546 );
1547
1548 if let Some(pic_inner_rect) = mapper.map(&local_inner_rect) {
1550 inner_rect = inner_rect.intersection(&pic_inner_rect).unwrap_or(PictureRect::zero());
1551 }
1552 }
1553 }
1554 }
1555
1556 Some(inner_rect)
1557 }
1558
1559 pub fn push_clip_instance(
1565 &mut self,
1566 handle: ClipDataHandle,
1567 spatial_node_index: SpatialNodeIndex,
1568 clip_rect: LayoutRect,
1569 ) -> ClipNodeRange {
1570 let first = self.clip_node_instances.len() as u32;
1571
1572 self.clip_node_instances.push(ClipNodeInstance {
1573 handle,
1574 spatial_node_index,
1575 clip_rect,
1576 flags: ClipNodeFlags::SAME_COORD_SYSTEM | ClipNodeFlags::SAME_SPATIAL_NODE,
1577 visible_tiles: None,
1578 });
1579
1580 ClipNodeRange {
1581 first,
1582 count: 1,
1583 }
1584 }
1585
1586 pub fn build_clip_chain_instance(
1589 &mut self,
1590 local_prim_rect: LayoutRect,
1591 prim_to_pic_mapper: &SpaceMapper<LayoutPixel, PicturePixel>,
1592 pic_to_vis_mapper: &SpaceMapper<PicturePixel, VisPixel>,
1593 spatial_tree: &SpatialTree,
1594 gpu_buffer: &mut GpuBufferBuilderF,
1595 resource_cache: &mut ResourceCache,
1596 culling_rect: &VisRect,
1597 clip_data_store: &mut ClipDataStore,
1598 rg_builder: &mut RenderTaskGraphBuilder,
1599 request_resources: bool,
1600 ) -> Option<ClipChainInstance> {
1601 let local_clip_rect = match self.active_local_clip_rect {
1602 Some(rect) => rect,
1603 None => return None,
1604 };
1605 profile_scope!("build_clip_chain_instance");
1606
1607
1608 let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?;
1609 let mut pic_coverage_rect = prim_to_pic_mapper.map(&local_bounding_rect)?;
1610 let vis_clip_rect = pic_to_vis_mapper.map(&pic_coverage_rect)?;
1611
1612 let first_clip_node_index = self.clip_node_instances.len() as u32;
1618 let mut has_non_local_clips = false;
1619 let mut needs_mask = false;
1620
1621 for node_info in self.active_clip_node_info.drain(..) {
1623 let node = &mut clip_data_store[node_info.handle];
1624
1625 let clip_result = match node_info.conversion {
1627 ClipSpaceConversion::Local => {
1628 node.item.kind.get_clip_result(&local_bounding_rect, node_info.clip_rect)
1629 }
1630 ClipSpaceConversion::ScaleOffset(ref scale_offset) => {
1631 has_non_local_clips = true;
1632 node.item.kind.get_clip_result(&scale_offset.unmap_rect(&local_bounding_rect), node_info.clip_rect)
1633 }
1634 ClipSpaceConversion::Transform(ref transform) => {
1635 has_non_local_clips = true;
1636 node.item.kind.get_clip_result_complex(
1637 transform,
1638 &vis_clip_rect,
1639 culling_rect,
1640 node_info.clip_rect,
1641 )
1642 }
1643 };
1644
1645 match clip_result {
1646 ClipResult::Accept => {
1647 }
1649 ClipResult::Reject => {
1650 return None;
1652 }
1653 ClipResult::Partial => {
1654 if let Some(instance) = node_info.create_instance(
1658 node,
1659 &local_bounding_rect,
1660 gpu_buffer,
1661 resource_cache,
1662 &mut self.mask_tiles,
1663 spatial_tree,
1664 rg_builder,
1665 request_resources,
1666 ) {
1667 needs_mask |= match node.item.kind {
1674 ClipItemKind::Rectangle { mode: ClipMode::ClipOut, .. } |
1675 ClipItemKind::RoundedRectangle { .. } |
1676 ClipItemKind::Image { .. } => {
1677 true
1678 }
1679
1680 ClipItemKind::Rectangle { mode: ClipMode::Clip, .. } => {
1681 !instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM)
1682 }
1683 };
1684
1685 self.clip_node_instances.push(instance);
1687 }
1688 }
1689 }
1690 }
1691
1692 let clips_range = ClipNodeRange {
1694 first: first_clip_node_index,
1695 count: self.clip_node_instances.len() as u32 - first_clip_node_index,
1696 };
1697
1698 if needs_mask {
1705 pic_coverage_rect = pic_coverage_rect.intersection(&self.active_pic_coverage_rect)?;
1706 }
1707
1708 Some(ClipChainInstance {
1710 clips_range,
1711 has_non_local_clips,
1712 local_clip_rect,
1713 pic_coverage_rect,
1714 pic_spatial_node_index: prim_to_pic_mapper.ref_spatial_node_index,
1715 needs_mask,
1716 })
1717 }
1718
1719 pub fn begin_frame(&mut self, scratch: &mut ClipStoreScratchBuffer) {
1720 mem::swap(&mut self.clip_node_instances, &mut scratch.clip_node_instances);
1721 mem::swap(&mut self.mask_tiles, &mut scratch.mask_tiles);
1722 self.clip_node_instances.clear();
1723 self.mask_tiles.clear();
1724 }
1725
1726 pub fn end_frame(&mut self, scratch: &mut ClipStoreScratchBuffer) {
1727 mem::swap(&mut self.clip_node_instances, &mut scratch.clip_node_instances);
1728 mem::swap(&mut self.mask_tiles, &mut scratch.mask_tiles);
1729 }
1730
1731 pub fn visible_mask_tiles(&self, instance: &ClipNodeInstance) -> &[VisibleMaskImageTile] {
1732 if let Some(range) = &instance.visible_tiles {
1733 &self.mask_tiles[range.clone()]
1734 } else {
1735 &[]
1736 }
1737 }
1738}
1739
1740impl Default for ClipStore {
1741 fn default() -> Self {
1742 ClipStore::new()
1743 }
1744}
1745
1746#[derive(Copy, Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
1758#[cfg_attr(feature = "capture", derive(Serialize))]
1759#[cfg_attr(feature = "replay", derive(Deserialize))]
1760pub enum ClipItemKeyKind {
1761 Rectangle(ClipMode),
1762 RoundedRectangle(BorderRadiusAu, ClipMode),
1763 ImageMask(ImageKey, Option<PolygonDataHandle>),
1764}
1765
1766impl ClipItemKeyKind {
1767 pub fn rectangle(mode: ClipMode) -> Self {
1768 ClipItemKeyKind::Rectangle(mode)
1769 }
1770
1771 pub fn rounded_rect(radii: BorderRadius, mode: ClipMode) -> Self {
1772 if radii.is_zero() {
1773 ClipItemKeyKind::rectangle(mode)
1774 } else {
1775 ClipItemKeyKind::RoundedRectangle(
1776 radii.into(),
1777 mode,
1778 )
1779 }
1780 }
1781
1782 pub fn image_mask(image_mask: &ImageMask,
1783 polygon_handle: Option<PolygonDataHandle>) -> Self {
1784 ClipItemKeyKind::ImageMask(
1785 image_mask.image,
1786 polygon_handle,
1787 )
1788 }
1789
1790 pub fn node_kind(&self) -> ClipNodeKind {
1791 match *self {
1792 ClipItemKeyKind::Rectangle(ClipMode::Clip) => ClipNodeKind::Rectangle,
1793
1794 ClipItemKeyKind::Rectangle(ClipMode::ClipOut) |
1795 ClipItemKeyKind::RoundedRectangle(..) |
1796 ClipItemKeyKind::ImageMask(..) => ClipNodeKind::Complex,
1797 }
1798 }
1799}
1800
1801#[derive(Debug, Copy, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
1802#[cfg_attr(feature = "capture", derive(Serialize))]
1803#[cfg_attr(feature = "replay", derive(Deserialize))]
1804pub struct ClipItemKey {
1805 pub kind: ClipItemKeyKind,
1806}
1807
1808#[derive(Copy, Clone)]
1810pub struct ClipItemEntry {
1811 pub key: ClipItemKey,
1812 pub spatial_node_index: SpatialNodeIndex,
1813 pub clip_rect: LayoutRect,
1814}
1815
1816#[derive(Debug, MallocSizeOf)]
1818#[cfg_attr(feature = "capture", derive(Serialize))]
1819#[cfg_attr(feature = "replay", derive(Deserialize))]
1820pub struct ClipInternData {
1821 pub key: ClipItemKey,
1822}
1823
1824impl intern::InternDebug for ClipItemKey {}
1825
1826impl intern::Internable for ClipIntern {
1827 type Key = ClipItemKey;
1828 type StoreData = ClipNode;
1829 type InternData = ClipInternData;
1830 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_CLIPS;
1831}
1832
1833#[derive(Debug, MallocSizeOf)]
1834#[cfg_attr(feature = "capture", derive(Serialize))]
1835#[cfg_attr(feature = "replay", derive(Deserialize))]
1836pub enum ClipItemKind {
1837 Rectangle {
1838 mode: ClipMode,
1839 },
1840 RoundedRectangle {
1841 radius: BorderRadius,
1842 mode: ClipMode,
1843 },
1844 Image {
1845 image: ImageKey,
1846 polygon_handle: Option<PolygonDataHandle>,
1847 },
1848}
1849
1850#[derive(Debug, MallocSizeOf)]
1851#[cfg_attr(feature = "capture", derive(Serialize))]
1852#[cfg_attr(feature = "replay", derive(Deserialize))]
1853pub struct ClipItem {
1854 pub kind: ClipItemKind,
1855}
1856
1857pub fn clamped_radius(radius: &BorderRadius, size: LayoutSize) -> BorderRadius {
1861 let mut r = *radius;
1862 ensure_no_corner_overlap(&mut r, size);
1863 r
1864}
1865
1866impl ClipItemKind {
1867 fn supports_fast_path_rendering(&self, clip_rect: LayoutRect) -> bool {
1872 match *self {
1873 ClipItemKind::Rectangle { .. } |
1874 ClipItemKind::Image { .. } => {
1875 false
1876 }
1877 ClipItemKind::RoundedRectangle { ref radius, .. } => {
1878 radius.can_use_fast_path_in(&clip_rect)
1879 }
1880 }
1881 }
1882
1883 pub fn get_local_clip_rect(&self, clip_rect: LayoutRect) -> Option<LayoutRect> {
1888 match *self {
1889 ClipItemKind::Rectangle { mode: ClipMode::Clip } => Some(clip_rect),
1890 ClipItemKind::Rectangle { mode: ClipMode::ClipOut } => None,
1891 ClipItemKind::RoundedRectangle { mode: ClipMode::Clip, .. } => Some(clip_rect),
1892 ClipItemKind::RoundedRectangle { mode: ClipMode::ClipOut, .. } => None,
1893 ClipItemKind::Image { .. } => Some(clip_rect),
1894 }
1895 }
1896
1897 fn get_clip_result_complex(
1898 &self,
1899 transform: &LayoutToVisTransform,
1900 prim_rect: &VisRect,
1901 culling_rect: &VisRect,
1902 clip_rect: LayoutRect,
1903 ) -> ClipResult {
1904 let visible_rect = match prim_rect.intersection(culling_rect) {
1905 Some(rect) => rect,
1906 None => return ClipResult::Reject,
1907 };
1908
1909 let (clip_rect, inner_rect, mode) = match *self {
1910 ClipItemKind::Rectangle { mode } => {
1911 (clip_rect, Some(clip_rect), mode)
1912 }
1913 ClipItemKind::RoundedRectangle { ref radius, mode } => {
1914 let clamped = clamped_radius(radius, clip_rect.size());
1915 let inner_clip_rect = extract_inner_rect_safe(&clip_rect, &clamped);
1916 (clip_rect, inner_clip_rect, mode)
1917 }
1918 ClipItemKind::Image { .. } => {
1919 (clip_rect, None, ClipMode::Clip)
1920 }
1921 };
1922
1923 if let Some(ref inner_clip_rect) = inner_rect {
1924 if let Some(()) = projected_rect_contains(inner_clip_rect, transform, &visible_rect) {
1925 return match mode {
1926 ClipMode::Clip => ClipResult::Accept,
1927 ClipMode::ClipOut => ClipResult::Reject,
1928 };
1929 }
1930 }
1931
1932 match mode {
1933 ClipMode::Clip => {
1934 let outer_clip_rect = match project_rect(
1935 transform,
1936 &clip_rect,
1937 &culling_rect,
1938 ) {
1939 Some(outer_clip_rect) => outer_clip_rect,
1940 None => return ClipResult::Partial,
1941 };
1942
1943 match outer_clip_rect.intersection(prim_rect) {
1944 Some(..) => {
1945 ClipResult::Partial
1946 }
1947 None => {
1948 ClipResult::Reject
1949 }
1950 }
1951 }
1952 ClipMode::ClipOut => ClipResult::Partial,
1953 }
1954 }
1955
1956 fn get_clip_result(
1958 &self,
1959 prim_rect: &LayoutRect,
1960 clip_rect: LayoutRect,
1961 ) -> ClipResult {
1962 match *self {
1963 ClipItemKind::Rectangle { mode: ClipMode::Clip } => {
1964 let rect = clip_rect;
1965 if rect.contains_box(prim_rect) {
1966 return ClipResult::Accept;
1967 }
1968
1969 match rect.intersection(prim_rect) {
1970 Some(..) => {
1971 ClipResult::Partial
1972 }
1973 None => {
1974 ClipResult::Reject
1975 }
1976 }
1977 }
1978 ClipItemKind::Rectangle { mode: ClipMode::ClipOut } => {
1979 let rect = clip_rect;
1980 if rect.contains_box(prim_rect) {
1981 return ClipResult::Reject;
1982 }
1983
1984 match rect.intersection(prim_rect) {
1985 Some(_) => {
1986 ClipResult::Partial
1987 }
1988 None => {
1989 ClipResult::Accept
1990 }
1991 }
1992 }
1993 ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip } => {
1994 let rect = clip_rect;
1995 let radius = clamped_radius(radius, rect.size());
1996 if rounded_rectangle_contains_box_quick(&rect, &radius, &prim_rect) {
1999 return ClipResult::Accept;
2000 }
2001
2002 match rect.intersection(prim_rect) {
2003 Some(..) => {
2004 ClipResult::Partial
2005 }
2006 None => {
2007 ClipResult::Reject
2008 }
2009 }
2010 }
2011 ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::ClipOut } => {
2012 let rect = clip_rect;
2013 let radius = clamped_radius(radius, rect.size());
2014 if rounded_rectangle_contains_box_quick(&rect, &radius, &prim_rect) {
2017 return ClipResult::Reject;
2018 }
2019
2020 match rect.intersection(prim_rect) {
2021 Some(_) => {
2022 ClipResult::Partial
2023 }
2024 None => {
2025 ClipResult::Accept
2026 }
2027 }
2028 }
2029 ClipItemKind::Image { .. } => {
2030 let rect = clip_rect;
2031 match rect.intersection(prim_rect) {
2032 Some(..) => {
2033 ClipResult::Partial
2034 }
2035 None => {
2036 ClipResult::Reject
2037 }
2038 }
2039 }
2040 }
2041 }
2042}
2043
2044pub fn rounded_rectangle_contains_point(
2045 point: &LayoutPoint,
2046 rect: &LayoutRect,
2047 radii: &BorderRadius
2048) -> bool {
2049 if !rect.contains(*point) {
2050 return false;
2051 }
2052
2053 let top_left_center = rect.min + radii.top_left.to_vector();
2054 if top_left_center.x > point.x && top_left_center.y > point.y &&
2055 !Ellipse::new(radii.top_left).contains(*point - top_left_center.to_vector()) {
2056 return false;
2057 }
2058
2059 let bottom_right_center = rect.bottom_right() - radii.bottom_right.to_vector();
2060 if bottom_right_center.x < point.x && bottom_right_center.y < point.y &&
2061 !Ellipse::new(radii.bottom_right).contains(*point - bottom_right_center.to_vector()) {
2062 return false;
2063 }
2064
2065 let top_right_center = rect.top_right() +
2066 LayoutVector2D::new(-radii.top_right.width, radii.top_right.height);
2067 if top_right_center.x < point.x && top_right_center.y > point.y &&
2068 !Ellipse::new(radii.top_right).contains(*point - top_right_center.to_vector()) {
2069 return false;
2070 }
2071
2072 let bottom_left_center = rect.bottom_left() +
2073 LayoutVector2D::new(radii.bottom_left.width, -radii.bottom_left.height);
2074 if bottom_left_center.x > point.x && bottom_left_center.y < point.y &&
2075 !Ellipse::new(radii.bottom_left).contains(*point - bottom_left_center.to_vector()) {
2076 return false;
2077 }
2078
2079 true
2080}
2081
2082fn rounded_rectangle_contains_box_quick(
2086 container: &LayoutRect,
2087 radii: &BorderRadius,
2088 containee: &LayoutRect,
2089) -> bool {
2090 if !container.contains_box(containee) {
2091 return false;
2092 }
2093
2094 fn foul(point: LayoutPoint, corner: LayoutPoint) -> bool {
2097 point.x < corner.x && point.y < corner.y
2098 }
2099
2100 fn flip_x(pt: LayoutPoint) -> LayoutPoint {
2102 LayoutPoint { x: -pt.x, .. pt }
2103 }
2104
2105 fn flip_y(pt: LayoutPoint) -> LayoutPoint {
2107 LayoutPoint { y: -pt.y, .. pt }
2108 }
2109
2110 if foul(containee.top_left(), container.top_left() + radii.top_left) ||
2111 foul(flip_x(containee.top_right()), flip_x(container.top_right()) + radii.top_right) ||
2112 foul(flip_y(containee.bottom_left()), flip_y(container.bottom_left()) + radii.bottom_left) ||
2113 foul(-containee.bottom_right(), -container.bottom_right() + radii.bottom_right)
2114 {
2115 return false;
2116 }
2117
2118 true
2119}
2120
2121pub fn is_left_of_line(
2128 p_x: f32,
2129 p_y: f32,
2130 p0_x: f32,
2131 p0_y: f32,
2132 p1_x: f32,
2133 p1_y: f32,
2134) -> f32 {
2135 (p1_x - p0_x) * (p_y - p0_y) - (p_x - p0_x) * (p1_y - p0_y)
2136}
2137
2138pub fn polygon_contains_point(
2139 point: &LayoutPoint,
2140 rect: &LayoutRect,
2141 polygon: &PolygonKey,
2142) -> bool {
2143 if !rect.contains(*point) {
2144 return false;
2145 }
2146
2147 let p = LayoutPoint::new(point.x - rect.min.x, point.y - rect.min.y);
2150
2151 let mut winding_number: i32 = 0;
2153
2154 let count = polygon.point_count as usize;
2155
2156 for i in 0..count {
2157 let p0 = polygon.points[i];
2158 let p1 = polygon.points[(i + 1) % count];
2159
2160 if p0.y <= p.y {
2161 if p1.y > p.y {
2162 if is_left_of_line(p.x, p.y, p0.x, p0.y, p1.x, p1.y) > 0.0 {
2163 winding_number = winding_number + 1;
2164 }
2165 }
2166 } else if p1.y <= p.y {
2167 if is_left_of_line(p.x, p.y, p0.x, p0.y, p1.x, p1.y) < 0.0 {
2168 winding_number = winding_number - 1;
2169 }
2170 }
2171 }
2172
2173 match polygon.fill_rule {
2174 FillRule::Nonzero => winding_number != 0,
2175 FillRule::Evenodd => winding_number.abs() % 2 == 1,
2176 }
2177}
2178
2179pub fn projected_rect_contains(
2180 source_rect: &LayoutRect,
2181 transform: &LayoutToVisTransform,
2182 target_rect: &VisRect,
2183) -> Option<()> {
2184 let points = [
2185 transform.transform_point2d(source_rect.top_left())?,
2186 transform.transform_point2d(source_rect.top_right())?,
2187 transform.transform_point2d(source_rect.bottom_right())?,
2188 transform.transform_point2d(source_rect.bottom_left())?,
2189 ];
2190 let target_points = [
2191 target_rect.top_left(),
2192 target_rect.top_right(),
2193 target_rect.bottom_right(),
2194 target_rect.bottom_left(),
2195 ];
2196 for (a, b) in points
2198 .iter()
2199 .cloned()
2200 .zip(points[1..].iter().cloned().chain(iter::once(points[0])))
2201 {
2202 if a.approx_eq(&b) || target_points.iter().any(|&c| (b - a).cross(c - a) < 0.0) {
2207 return None
2208 }
2209 }
2210
2211 Some(())
2212}
2213
2214
2215fn add_clip_node_to_current_chain(
2219 handle: ClipDataHandle,
2220 clip_spatial_node_index: SpatialNodeIndex,
2221 clip_rect: LayoutRect,
2222 prim_spatial_node_index: SpatialNodeIndex,
2223 pic_spatial_node_index: SpatialNodeIndex,
2224 visibility_spatial_node_index: SpatialNodeIndex,
2225 local_clip_rect: &mut LayoutRect,
2226 clip_node_info: &mut Vec<ClipNodeInfo>,
2227 pic_coverage_rect: &mut PictureRect,
2228 clip_data_store: &ClipDataStore,
2229 spatial_tree: &SpatialTree,
2230) -> bool {
2231 let clip_node = &clip_data_store[handle];
2232
2233 let conversion = ClipSpaceConversion::new(
2236 prim_spatial_node_index,
2237 clip_spatial_node_index,
2238 visibility_spatial_node_index,
2239 spatial_tree,
2240 );
2241
2242 if let Some(clip_rect) = clip_node.item.kind.get_local_clip_rect(clip_rect) {
2245 match conversion {
2246 ClipSpaceConversion::Local => {
2247 *local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
2248 Some(rect) => rect,
2249 None => return false,
2250 };
2251 }
2252 ClipSpaceConversion::ScaleOffset(ref scale_offset) => {
2253 let clip_rect = scale_offset.map_rect(&clip_rect);
2254 *local_clip_rect = match local_clip_rect.intersection(&clip_rect) {
2255 Some(rect) => rect,
2256 None => return false,
2257 };
2258 }
2259 ClipSpaceConversion::Transform(..) => {
2260 let pic_coord_system = spatial_tree
2270 .get_spatial_node(pic_spatial_node_index)
2271 .coordinate_system_id;
2272
2273 let clip_coord_system = spatial_tree
2274 .get_spatial_node(clip_spatial_node_index)
2275 .coordinate_system_id;
2276
2277 if pic_coord_system == clip_coord_system {
2278 let mapper = SpaceMapper::new_with_target(
2279 pic_spatial_node_index,
2280 clip_spatial_node_index,
2281 PictureRect::max_rect(),
2282 spatial_tree,
2283 );
2284
2285 if let Some(pic_clip_rect) = mapper.map(&clip_rect) {
2286 *pic_coverage_rect = pic_clip_rect
2287 .intersection(pic_coverage_rect)
2288 .unwrap_or(PictureRect::zero());
2289 }
2290 }
2291 }
2292 }
2293 }
2294
2295 clip_node_info.push(ClipNodeInfo {
2296 conversion,
2297 handle,
2298 spatial_node_index: clip_spatial_node_index,
2299 clip_rect,
2300 });
2301
2302 true
2303}
2304
2305#[cfg(test)]
2306mod tests {
2307 use super::*;
2308 use super::projected_rect_contains;
2309 use euclid::{Transform3D, rect};
2310 use api::units::{LayoutRect, LayoutSize, LayoutPoint};
2311
2312 #[test]
2313 fn test_empty_projected_rect() {
2314 assert_eq!(
2315 None,
2316 projected_rect_contains(
2317 &rect(10.0, 10.0, 0.0, 0.0).to_box2d(),
2318 &Transform3D::identity(),
2319 &rect(20.0, 20.0, 10.0, 10.0).to_box2d(),
2320 ),
2321 "Empty rectangle is considered to include a non-empty!"
2322 );
2323 }
2324
2325 fn lr(x: f32, y: f32, w: f32, h: f32) -> LayoutRect {
2326 LayoutRect::from_origin_and_size(LayoutPoint::new(x, y), LayoutSize::new(w, h))
2327 }
2328
2329 fn uniform_radius(r: f32) -> BorderRadius {
2330 BorderRadius::uniform(r)
2331 }
2332
2333 fn per_corner_radius(tl: f32, tr: f32, bl: f32, br: f32) -> BorderRadius {
2334 BorderRadius {
2335 top_left: LayoutSize::new(tl, tl),
2336 top_right: LayoutSize::new(tr, tr),
2337 bottom_left: LayoutSize::new(bl, bl),
2338 bottom_right: LayoutSize::new(br, br),
2339 }
2340 }
2341
2342 #[test]
2343 fn test_intersect_identical() {
2344 let rect = lr(0.0, 0.0, 400.0, 400.0);
2345 let radius = uniform_radius(20.0);
2346 let result = intersect_rounded_rects(rect, radius, rect, radius);
2347 assert!(result.is_some());
2348 let (r, rad) = result.unwrap();
2349 assert_eq!(r, rect);
2350 assert_eq!(rad.top_left.width, 20.0);
2351 }
2352
2353 #[test]
2354 fn test_intersect_inner_fully_inside() {
2355 let outer = lr(0.0, 0.0, 400.0, 400.0);
2356 let inner = lr(50.0, 50.0, 300.0, 300.0);
2357 let result = intersect_rounded_rects(
2358 outer, uniform_radius(20.0),
2359 inner, uniform_radius(15.0),
2360 );
2361 assert!(result.is_some());
2362 let (r, rad) = result.unwrap();
2363 assert_eq!(r, inner);
2364 assert_eq!(rad.top_left.width, 15.0);
2365 assert_eq!(rad.bottom_right.width, 15.0);
2366 }
2367
2368 #[test]
2369 fn test_intersect_shared_top_different_bottom() {
2370 let outer = lr(0.0, 0.0, 400.0, 400.0);
2371 let inner = lr(0.0, 0.0, 400.0, 350.0);
2372 let result = intersect_rounded_rects(
2373 outer, uniform_radius(20.0),
2374 inner, uniform_radius(15.0),
2375 );
2376 assert!(result.is_some());
2377 let (r, rad) = result.unwrap();
2378 assert_eq!(r, inner);
2379 assert_eq!(rad.top_left.width, 20.0);
2380 assert_eq!(rad.top_right.width, 20.0);
2381 assert_eq!(rad.bottom_left.width, 15.0);
2382 assert_eq!(rad.bottom_right.width, 15.0);
2383 }
2384
2385 #[test]
2386 fn test_intersect_no_overlap() {
2387 let a = lr(0.0, 0.0, 100.0, 100.0);
2388 let b = lr(200.0, 200.0, 100.0, 100.0);
2389 let result = intersect_rounded_rects(a, uniform_radius(10.0), b, uniform_radius(10.0));
2390 assert!(result.is_none());
2391 }
2392
2393 #[test]
2394 fn test_intersect_encroaching_corner() {
2395 let outer = lr(0.0, 0.0, 400.0, 400.0);
2396 let inner = lr(10.0, 10.0, 380.0, 380.0);
2397 let result = intersect_rounded_rects(
2398 outer, uniform_radius(200.0),
2399 inner, uniform_radius(15.0),
2400 );
2401 assert!(result.is_none());
2402 }
2403
2404 #[test]
2405 fn test_intersect_zero_radius_no_encroach() {
2406 let outer = lr(0.0, 0.0, 400.0, 400.0);
2407 let inner = lr(50.0, 50.0, 300.0, 300.0);
2408 let result = intersect_rounded_rects(
2409 outer, uniform_radius(20.0),
2410 inner, BorderRadius::zero(),
2411 );
2412 assert!(result.is_some());
2413 let (_, rad) = result.unwrap();
2414 assert_eq!(rad.top_left.width, 0.0);
2415 assert_eq!(rad.bottom_right.width, 0.0);
2416 }
2417
2418 #[test]
2419 fn test_intersect_linux_window_corners() {
2420 let window = lr(0.0, 0.0, 1920.0, 1080.0);
2421 let content = lr(0.0, 40.0, 1920.0, 1040.0);
2422 let window_radius = uniform_radius(10.0);
2423 let content_radius = per_corner_radius(8.0, 0.0, 0.0, 0.0);
2424
2425 let result = intersect_rounded_rects(window, window_radius, content, content_radius);
2426 assert!(result.is_some());
2427 let (r, rad) = result.unwrap();
2428 assert_eq!(r, content);
2429 assert_eq!(rad.top_left.width, 8.0);
2430 assert_eq!(rad.top_right.width, 0.0);
2431 assert_eq!(rad.bottom_left.width, 10.0);
2432 assert_eq!(rad.bottom_right.width, 10.0);
2433 }
2434}
2435
2436pub fn intersect_rounded_rects(
2441 rect_a: LayoutRect,
2442 radius_a: BorderRadius,
2443 rect_b: LayoutRect,
2444 radius_b: BorderRadius,
2445) -> Option<(LayoutRect, BorderRadius)> {
2446 let result_rect = rect_a.intersection(&rect_b)?;
2447 if result_rect.is_empty() {
2448 return None;
2449 }
2450
2451 let result_radius = BorderRadius {
2452 top_left: resolve_corner_radius(
2453 result_rect.min.x, result_rect.min.y,
2454 rect_a.min.x, rect_a.min.y, radius_a.top_left,
2455 rect_b.min.x, rect_b.min.y, radius_b.top_left,
2456 1.0, 1.0,
2457 )?,
2458 top_right: resolve_corner_radius(
2459 result_rect.max.x, result_rect.min.y,
2460 rect_a.max.x, rect_a.min.y, radius_a.top_right,
2461 rect_b.max.x, rect_b.min.y, radius_b.top_right,
2462 -1.0, 1.0,
2463 )?,
2464 bottom_left: resolve_corner_radius(
2465 result_rect.min.x, result_rect.max.y,
2466 rect_a.min.x, rect_a.max.y, radius_a.bottom_left,
2467 rect_b.min.x, rect_b.max.y, radius_b.bottom_left,
2468 1.0, -1.0,
2469 )?,
2470 bottom_right: resolve_corner_radius(
2471 result_rect.max.x, result_rect.max.y,
2472 rect_a.max.x, rect_a.max.y, radius_a.bottom_right,
2473 rect_b.max.x, rect_b.max.y, radius_b.bottom_right,
2474 -1.0, -1.0,
2475 )?,
2476 };
2477
2478 if !result_radius.can_use_fast_path_in(&result_rect) {
2479 return None;
2480 }
2481
2482 Some((result_rect, result_radius))
2483}
2484
2485fn resolve_corner_radius(
2492 ix: f32, iy: f32,
2493 ax: f32, ay: f32, ra: LayoutSize,
2494 bx: f32, by: f32, rb: LayoutSize,
2495 sx: f32, sy: f32,
2496) -> Option<LayoutSize> {
2497 let a_matches = ax == ix && ay == iy;
2498 let b_matches = bx == ix && by == iy;
2499
2500 match (a_matches, b_matches) {
2501 (true, true) => {
2502 Some(LayoutSize::new(ra.width.max(rb.width), ra.height.max(rb.height)))
2503 }
2504 (true, false) => {
2505 if corner_encroaches(ix, iy, bx, by, rb, sx, sy) {
2506 None
2507 } else {
2508 Some(ra)
2509 }
2510 }
2511 (false, true) => {
2512 if corner_encroaches(ix, iy, ax, ay, ra, sx, sy) {
2513 None
2514 } else {
2515 Some(rb)
2516 }
2517 }
2518 (false, false) => {
2519 if corner_encroaches(ix, iy, ax, ay, ra, sx, sy) ||
2520 corner_encroaches(ix, iy, bx, by, rb, sx, sy) {
2521 None
2522 } else {
2523 Some(LayoutSize::zero())
2524 }
2525 }
2526 }
2527}
2528
2529fn corner_encroaches(
2533 ix: f32, iy: f32,
2534 cx: f32, cy: f32,
2535 r: LayoutSize,
2536 sx: f32, sy: f32,
2537) -> bool {
2538 if r.width == 0.0 || r.height == 0.0 {
2539 return false;
2540 }
2541 let dx = sx * (ix - cx);
2542 let dy = sy * (iy - cy);
2543 r.width > dx && r.height > dy
2544}
2545
2546#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
2553#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))]
2554pub enum PolygonIntern {}
2555
2556pub type PolygonDataHandle = intern::Handle<PolygonIntern>;
2557
2558impl intern::InternDebug for PolygonKey {}
2559
2560impl intern::Internable for PolygonIntern {
2561 type Key = PolygonKey;
2562 type StoreData = PolygonKey;
2563 type InternData = PolygonKey;
2564 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_POLYGONS;
2565}