1use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, BuiltDisplayListIter, PrimitiveFlags, SnapshotInfo};
39use api::{ClipId, ColorF, CommonItemProperties, ComplexClipRegion, ComponentTransferFuncType, RasterSpace};
40use api::{DebugFlags, DisplayItem, DisplayItemRef, ExtendMode, ExternalScrollId, FilterData};
41use api::{FilterOp, FilterPrimitive, FontInstanceKey, FontSize, GlyphInstance, GlyphOptions, GradientStop};
42use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, ColorDepth, QualitySettings};
43use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId, MixBlendMode, StackingContextFlags};
44use api::{PropertyBinding, ReferenceFrameKind, ScrollFrameDescriptor};
45use api::{APZScrollGeneration, HasScrollLinkedEffect, Shadow, SpatialId, StickyFrameDescriptor, ImageMask, ItemTag};
46use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange, YuvData, TempFilterData};
47use api::{ReferenceTransformBinding, Rotation, FillRule, SpatialTreeItem, ReferenceFrameDescriptor};
48use api::{FilterOpGraphPictureBufferId, SVGFE_GRAPH_MAX};
49use api::channel::{unbounded_channel, Receiver, Sender};
50use api::units::*;
51use crate::image_tiling::simplify_repeated_primitive;
52use crate::box_shadow::BLUR_SAMPLE_SCALE;
53use crate::clip::{ClipIntern, ClipItemKey, ClipItemKeyKind, ClipStore};
54use crate::clip::{ClipInternData, ClipNodeId, ClipLeafId};
55use crate::clip::{PolygonDataHandle, ClipTreeBuilder};
56use crate::gpu_types::BlurEdgeMode;
57use crate::segment::EdgeAaSegmentMask;
58use crate::spatial_tree::{SceneSpatialTree, SpatialNodeContainer, SpatialNodeIndex, get_external_scroll_offset};
59use crate::frame_builder::FrameBuilderConfig;
60use glyph_rasterizer::{FontInstance, SharedFontResources};
61use crate::hit_test::HitTestingScene;
62use crate::intern::Interner;
63use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter, FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, PlaneSplitterIndex, PipelineInstanceId};
64use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive};
65use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList, SurfaceInfo, PictureFlags};
66use crate::picture_graph::PictureGraph;
67use crate::prim_store::{PrimitiveInstance, PrimitiveStoreStats};
68use crate::prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore};
69use crate::prim_store::{InternablePrimitive, PictureIndex};
70use crate::prim_store::PolygonKey;
71use crate::prim_store::backdrop::{BackdropCapture, BackdropRender};
72use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
73use crate::prim_store::gradient::{
74    GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient,
75    ConicGradientParams, optimize_radial_gradient, apply_gradient_local_clip,
76    optimize_linear_gradient, self,
77};
78use crate::prim_store::image::{Image, YuvImage};
79use crate::prim_store::line_dec::{LineDecoration, LineDecorationCacheKey, get_line_decoration_size};
80use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey};
81use crate::prim_store::text_run::TextRun;
82use crate::render_backend::SceneView;
83use crate::resource_cache::ImageRequest;
84use crate::scene::{BuiltScene, Scene, ScenePipeline, SceneStats, StackingContextHelpers};
85use crate::scene_builder_thread::Interners;
86use crate::space::SpaceSnapper;
87use crate::spatial_node::{
88    ReferenceFrameInfo, StickyFrameInfo, ScrollFrameKind, SpatialNodeUid, SpatialNodeType
89};
90use crate::tile_cache::TileCacheBuilder;
91use euclid::approxeq::ApproxEq;
92use std::{f32, mem, usize};
93use std::collections::vec_deque::VecDeque;
94use std::sync::Arc;
95use crate::util::{VecHelper, MaxRect};
96use crate::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
97use log::Level;
98
99pub struct ScrollOffsetMapper {
102    pub current_spatial_node: SpatialNodeIndex,
103    pub current_offset: LayoutVector2D,
104}
105
106impl ScrollOffsetMapper {
107    fn new() -> Self {
108        ScrollOffsetMapper {
109            current_spatial_node: SpatialNodeIndex::INVALID,
110            current_offset: LayoutVector2D::zero(),
111        }
112    }
113
114    fn external_scroll_offset(
118        &mut self,
119        spatial_node_index: SpatialNodeIndex,
120        spatial_tree: &SceneSpatialTree,
121    ) -> LayoutVector2D {
122        if spatial_node_index != self.current_spatial_node {
123            self.current_spatial_node = spatial_node_index;
124            self.current_offset = get_external_scroll_offset(spatial_tree, spatial_node_index);
125        }
126
127        self.current_offset
128    }
129}
130
131#[derive(Default)]
135pub struct NodeIdToIndexMapper {
136    spatial_node_map: FastHashMap<SpatialId, SpatialNodeIndex>,
137}
138
139impl NodeIdToIndexMapper {
140    fn add_spatial_node(&mut self, id: SpatialId, index: SpatialNodeIndex) {
141        let _old_value = self.spatial_node_map.insert(id, index);
142        assert!(_old_value.is_none());
143    }
144
145    fn get_spatial_node_index(&self, id: SpatialId) -> SpatialNodeIndex {
146        self.spatial_node_map[&id]
147    }
148}
149
150#[derive(Debug, Clone, Default)]
151pub struct CompositeOps {
152    pub filters: Vec<Filter>,
154    pub filter_datas: Vec<FilterData>,
155    pub filter_primitives: Vec<FilterPrimitive>,
156    pub snapshot: Option<SnapshotInfo>,
157
158    pub mix_blend_mode: Option<MixBlendMode>,
160}
161
162impl CompositeOps {
163    pub fn new(
164        filters: Vec<Filter>,
165        filter_datas: Vec<FilterData>,
166        filter_primitives: Vec<FilterPrimitive>,
167        mix_blend_mode: Option<MixBlendMode>,
168        snapshot: Option<SnapshotInfo>,
169    ) -> Self {
170        CompositeOps {
171            filters,
172            filter_datas,
173            filter_primitives,
174            mix_blend_mode,
175            snapshot,
176        }
177    }
178
179    pub fn is_empty(&self) -> bool {
180        self.filters.is_empty() &&
181            self.filter_primitives.is_empty() &&
182            self.mix_blend_mode.is_none() &&
183            self.snapshot.is_none()
184    }
185
186    fn has_valid_filters(&self) -> bool {
189        let mut current_filter_data_index = 0;
191        for filter in &self.filters {
192            match filter {
193                Filter::ComponentTransfer => {
194                    let filter_data =
195                        &self.filter_datas[current_filter_data_index];
196                    let filter_data = filter_data.sanitize();
197                    current_filter_data_index = current_filter_data_index + 1;
198                    if filter_data.is_identity() {
199                        continue
200                    } else {
201                        return true;
202                    }
203                }
204                Filter::SVGGraphNode(..) => {return true;}
205                _ => {
206                    if filter.is_noop() {
207                        continue;
208                    } else {
209                        return true;
210                    }
211                }
212            }
213        }
214
215        if !self.filter_primitives.is_empty() {
216            return true;
217        }
218
219        false
220    }
221}
222
223enum PictureSource {
226    PrimitiveList {
227        prim_list: PrimitiveList,
228    },
229    WrappedPicture {
230        instance: PrimitiveInstance,
231    },
232}
233
234struct PictureChainBuilder {
237    current: PictureSource,
239
240    spatial_node_index: SpatialNodeIndex,
242    flags: PrimitiveFlags,
244    raster_space: RasterSpace,
246    set_resolve_target: bool,
248    establishes_sub_graph: bool,
250}
251
252impl PictureChainBuilder {
253    fn from_prim_list(
255        prim_list: PrimitiveList,
256        flags: PrimitiveFlags,
257        spatial_node_index: SpatialNodeIndex,
258        raster_space: RasterSpace,
259        is_sub_graph: bool,
260    ) -> Self {
261        PictureChainBuilder {
262            current: PictureSource::PrimitiveList {
263                prim_list,
264            },
265            spatial_node_index,
266            flags,
267            raster_space,
268            establishes_sub_graph: is_sub_graph,
269            set_resolve_target: is_sub_graph,
270        }
271    }
272
273    fn from_instance(
275        instance: PrimitiveInstance,
276        flags: PrimitiveFlags,
277        spatial_node_index: SpatialNodeIndex,
278        raster_space: RasterSpace,
279    ) -> Self {
280        PictureChainBuilder {
281            current: PictureSource::WrappedPicture {
282                instance,
283            },
284            flags,
285            spatial_node_index,
286            raster_space,
287            establishes_sub_graph: false,
288            set_resolve_target: false,
289        }
290    }
291
292    #[must_use]
294    fn add_picture(
295        self,
296        composite_mode: PictureCompositeMode,
297        clip_node_id: ClipNodeId,
298        context_3d: Picture3DContext<OrderedPictureChild>,
299        interners: &mut Interners,
300        prim_store: &mut PrimitiveStore,
301        prim_instances: &mut Vec<PrimitiveInstance>,
302        clip_tree_builder: &mut ClipTreeBuilder,
303    ) -> PictureChainBuilder {
304        let prim_list = match self.current {
305            PictureSource::PrimitiveList { prim_list } => {
306                prim_list
307            }
308            PictureSource::WrappedPicture { instance } => {
309                let mut prim_list = PrimitiveList::empty();
310
311                prim_list.add_prim(
312                    instance,
313                    LayoutRect::zero(),
314                    self.spatial_node_index,
315                    self.flags,
316                    prim_instances,
317                    clip_tree_builder,
318                );
319
320                prim_list
321            }
322        };
323
324        let flags = if self.set_resolve_target {
325            PictureFlags::IS_RESOLVE_TARGET
326        } else {
327            PictureFlags::empty()
328        };
329
330        let pic_index = PictureIndex(prim_store.pictures
331            .alloc()
332            .init(PicturePrimitive::new_image(
333                Some(composite_mode.clone()),
334                context_3d,
335                self.flags,
336                prim_list,
337                self.spatial_node_index,
338                self.raster_space,
339                flags,
340                None,
341            ))
342        );
343
344        let instance = create_prim_instance(
345            pic_index,
346            Some(composite_mode).into(),
347            self.raster_space,
348            clip_node_id,
349            interners,
350            clip_tree_builder,
351        );
352
353        PictureChainBuilder {
354            current: PictureSource::WrappedPicture {
355                instance,
356            },
357            spatial_node_index: self.spatial_node_index,
358            flags: self.flags,
359            raster_space: self.raster_space,
360            set_resolve_target: false,
362            establishes_sub_graph: self.establishes_sub_graph,
363        }
364    }
365
366    fn finalize(
368        self,
369        clip_node_id: ClipNodeId,
370        interners: &mut Interners,
371        prim_store: &mut PrimitiveStore,
372        clip_tree_builder: &mut ClipTreeBuilder,
373        snapshot: Option<SnapshotInfo>,
374    ) -> PrimitiveInstance {
375        let mut flags = PictureFlags::empty();
376        if self.establishes_sub_graph {
377            flags |= PictureFlags::IS_SUB_GRAPH;
378        }
379
380        match self.current {
381            PictureSource::WrappedPicture { instance } => {
382                let pic_index = instance.kind.as_pic();
383                let picture = &mut prim_store.pictures[pic_index.0];
384                picture.flags |= flags;
385                picture.snapshot = snapshot;
386
387                instance
388            }
389            PictureSource::PrimitiveList { prim_list } => {
390                if self.set_resolve_target {
391                    flags |= PictureFlags::IS_RESOLVE_TARGET;
392                }
393
394                let composite_mode = snapshot.map(|_| PictureCompositeMode::Blit(BlitReason::SNAPSHOT));
401
402                let pic_index = PictureIndex(prim_store.pictures
403                    .alloc()
404                    .init(PicturePrimitive::new_image(
405                        composite_mode,
406                        Picture3DContext::Out,
407                        self.flags,
408                        prim_list,
409                        self.spatial_node_index,
410                        self.raster_space,
411                        flags,
412                        snapshot,
413                    ))
414                );
415
416                create_prim_instance(
417                    pic_index,
418                    None.into(),
419                    self.raster_space,
420                    clip_node_id,
421                    interners,
422                    clip_tree_builder,
423                )
424            }
425        }
426    }
427
428    #[allow(dead_code)]
430    fn has_picture(&self) -> bool {
431        match self.current {
432            PictureSource::WrappedPicture { .. } => true,
433            PictureSource::PrimitiveList { .. } => false,
434        }
435    }
436}
437
438bitflags! {
439    #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
441    pub struct SliceFlags : u8 {
442        const IS_SCROLLBAR = 1;
444        const IS_ATOMIC = 2;
446    }
447}
448
449pub struct SceneBuilder<'a> {
453    scene: &'a Scene,
455
456    fonts: SharedFontResources,
458
459    id_to_index_mapper_stack: Vec<NodeIdToIndexMapper>,
462
463    sc_stack: Vec<FlattenedStackingContext>,
465
466    containing_block_stack: Vec<SpatialNodeIndex>,
468
469    raster_space_stack: Vec<RasterSpace>,
471
472    pending_shadow_items: VecDeque<ShadowItem>,
474
475    pub spatial_tree: &'a mut SceneSpatialTree,
477
478    pub prim_store: PrimitiveStore,
480
481    pub hit_testing_scene: HitTestingScene,
483
484    pub clip_store: ClipStore,
486
487    pub config: FrameBuilderConfig,
490
491    pub interners: &'a mut Interners,
493
494    external_scroll_mapper: ScrollOffsetMapper,
496
497    iframe_size: Vec<LayoutSize>,
500
501    root_iframe_clip: Option<ClipId>,
503
504    quality_settings: QualitySettings,
506
507    tile_cache_builder: TileCacheBuilder,
509
510    snap_to_device: SpaceSnapper,
516
517    picture_graph: PictureGraph,
522
523    snapshot_pictures: Vec<PictureIndex>,
526
527    next_plane_splitter_index: usize,
535
536    prim_instances: Vec<PrimitiveInstance>,
540
541    pipeline_instance_ids: FastHashMap<PipelineId, u32>,
544
545    surfaces: Vec<SurfaceInfo>,
549
550    clip_tree_builder: ClipTreeBuilder,
552
553    extra_stacking_context_stack: Vec<StackingContextInfo>,
558}
559
560impl<'a> SceneBuilder<'a> {
561    pub fn build(
562        scene: &Scene,
563        root_pipeline: Option<PipelineId>,
564        fonts: SharedFontResources,
565        view: &SceneView,
566        frame_builder_config: &FrameBuilderConfig,
567        interners: &mut Interners,
568        spatial_tree: &mut SceneSpatialTree,
569        recycler: &mut SceneRecycler,
570        stats: &SceneStats,
571        debug_flags: DebugFlags,
572    ) -> BuiltScene {
573        profile_scope!("build_scene");
574
575        let root_pipeline_id = root_pipeline.or(scene.root_pipeline_id).unwrap();
577        let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
578        let root_reference_frame_index = spatial_tree.root_reference_frame_index();
579
580        let snap_to_device = SpaceSnapper::new(
582            root_reference_frame_index,
583            RasterPixelScale::new(1.0),
584        );
585
586        let mut builder = SceneBuilder {
587            scene,
588            spatial_tree,
589            fonts,
590            config: *frame_builder_config,
591            id_to_index_mapper_stack: mem::take(&mut recycler.id_to_index_mapper_stack),
592            hit_testing_scene: recycler.hit_testing_scene.take().unwrap_or_else(|| HitTestingScene::new(&stats.hit_test_stats)),
593            pending_shadow_items: mem::take(&mut recycler.pending_shadow_items),
594            sc_stack: mem::take(&mut recycler.sc_stack),
595            containing_block_stack: mem::take(&mut recycler.containing_block_stack),
596            raster_space_stack: mem::take(&mut recycler.raster_space_stack),
597            prim_store: mem::take(&mut recycler.prim_store),
598            clip_store: mem::take(&mut recycler.clip_store),
599            interners,
600            external_scroll_mapper: ScrollOffsetMapper::new(),
601            iframe_size: mem::take(&mut recycler.iframe_size),
602            root_iframe_clip: None,
603            quality_settings: view.quality_settings,
604            tile_cache_builder: TileCacheBuilder::new(
605                root_reference_frame_index,
606                frame_builder_config.background_color,
607                debug_flags,
608            ),
609            snap_to_device,
610            picture_graph: mem::take(&mut recycler.picture_graph),
611            snapshot_pictures: Vec::new(),
613            next_plane_splitter_index: 0,
614            prim_instances: mem::take(&mut recycler.prim_instances),
615            pipeline_instance_ids: FastHashMap::default(),
616            surfaces: mem::take(&mut recycler.surfaces),
617            clip_tree_builder: recycler.clip_tree_builder.take().unwrap_or_else(|| ClipTreeBuilder::new()),
618            extra_stacking_context_stack: Vec::new(),
619        };
620
621        builder.hit_testing_scene.reset();
623        builder.prim_store.reset();
624        builder.clip_store.reset();
625        builder.picture_graph.reset();
626        builder.prim_instances.clear();
627        builder.surfaces.clear();
628        builder.sc_stack.clear();
629        builder.containing_block_stack.clear();
630        builder.id_to_index_mapper_stack.clear();
631        builder.pending_shadow_items.clear();
632        builder.iframe_size.clear();
633
634        builder.raster_space_stack.clear();
635        builder.raster_space_stack.push(RasterSpace::Screen);
636
637        builder.clip_tree_builder.begin();
638
639        builder.build_all(
640            root_pipeline_id,
641            &root_pipeline,
642        );
643
644        let (tile_cache_config, tile_cache_pictures) = builder.tile_cache_builder.build(
646            &builder.config,
647            &mut builder.prim_store,
648            &builder.spatial_tree,
649            &builder.prim_instances,
650            &mut builder.clip_tree_builder,
651            &builder.interners,
652        );
653
654        for pic_index in &builder.snapshot_pictures {
655            builder.picture_graph.add_root(*pic_index);
656        }
657
658        for pic_index in &tile_cache_pictures {
660            builder.picture_graph.add_root(*pic_index);
661            SceneBuilder::finalize_picture(
662                *pic_index,
663                None,
664                &mut builder.prim_store.pictures,
665                None,
666                &builder.clip_tree_builder,
667                &builder.prim_instances,
668                &builder.interners.clip,
669            );
670        }
671
672        let clip_tree = builder.clip_tree_builder.finalize();
673
674        recycler.clip_tree_builder = Some(builder.clip_tree_builder);
675        recycler.sc_stack = builder.sc_stack;
676        recycler.id_to_index_mapper_stack = builder.id_to_index_mapper_stack;
677        recycler.containing_block_stack = builder.containing_block_stack;
678        recycler.raster_space_stack = builder.raster_space_stack;
679        recycler.pending_shadow_items = builder.pending_shadow_items;
680        recycler.iframe_size = builder.iframe_size;
681
682        BuiltScene {
683            has_root_pipeline: scene.has_root_pipeline(),
684            pipeline_epochs: scene.pipeline_epochs.clone(),
685            output_rect: view.device_rect.size().into(),
686            hit_testing_scene: Arc::new(builder.hit_testing_scene),
687            prim_store: builder.prim_store,
688            clip_store: builder.clip_store,
689            config: builder.config,
690            tile_cache_config,
691            snapshot_pictures: builder.snapshot_pictures,
692            tile_cache_pictures,
693            picture_graph: builder.picture_graph,
694            num_plane_splitters: builder.next_plane_splitter_index,
695            prim_instances: builder.prim_instances,
696            surfaces: builder.surfaces,
697            clip_tree,
698            recycler_tx: Some(recycler.tx.clone()),
699        }
700    }
701
702    fn finalize_picture(
710        pic_index: PictureIndex,
711        prim_index: Option<usize>,
712        pictures: &mut [PicturePrimitive],
713        parent_spatial_node_index: Option<SpatialNodeIndex>,
714        clip_tree_builder: &ClipTreeBuilder,
715        prim_instances: &[PrimitiveInstance],
716        clip_interner: &Interner<ClipIntern>,
717    ) {
718        let (mut prim_list, spatial_node_index) = {
721            let pic = &mut pictures[pic_index.0];
722            assert_ne!(pic.spatial_node_index, SpatialNodeIndex::UNKNOWN);
723
724            if pic.flags.contains(PictureFlags::IS_RESOLVE_TARGET) {
725                pic.flags |= PictureFlags::DISABLE_SNAPPING;
726            }
727
728            let spatial_node_index = match pic.composite_mode {
730                Some(_) => pic.spatial_node_index,
731                None => parent_spatial_node_index.expect("bug: no parent"),
732            };
733
734            (
735                mem::replace(&mut pic.prim_list, PrimitiveList::empty()),
736                spatial_node_index,
737            )
738        };
739
740        for cluster in &mut prim_list.clusters {
742            if cluster.spatial_node_index == SpatialNodeIndex::UNKNOWN {
743                cluster.spatial_node_index = spatial_node_index;
744            }
745        }
746
747        let mut shared_clip_node_id = None;
756
757        let is_snapshot = pictures[pic_index.0].snapshot.is_some();
762
763        if is_snapshot {
764            if let Some(idx) = prim_index {
775                let clip_node = clip_tree_builder.get_leaf(prim_instances[idx].clip_leaf_id).node_id;
776                shared_clip_node_id = clip_tree_builder.get_parent(clip_node);
777            }
778        } else {
779            for cluster in &prim_list.clusters {
780                for prim_instance in &prim_instances[cluster.prim_range()] {
781                    let leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
782
783                    shared_clip_node_id = match shared_clip_node_id {
784                        Some(current) => {
785                            Some(clip_tree_builder.find_lowest_common_ancestor(
786                                current,
787                                leaf.node_id,
788                            ))
789                        }
790                        None => Some(leaf.node_id)
791                    };
792                }
793            }
794        }
795
796        let lca_tree_node = shared_clip_node_id
797            .and_then(|node_id| (node_id != ClipNodeId::NONE).then_some(node_id))
798            .map(|node_id| clip_tree_builder.get_node(node_id));
799        let lca_node = lca_tree_node
800            .map(|tree_node| &clip_interner[tree_node.handle]);
801        let pic_node_id = prim_index
802            .map(|prim_index| clip_tree_builder.get_leaf(prim_instances[prim_index].clip_leaf_id).node_id)
803            .and_then(|node_id| (node_id != ClipNodeId::NONE).then_some(node_id));
804        let pic_node = pic_node_id
805            .map(|node_id| clip_tree_builder.get_node(node_id))
806            .map(|tree_node| &clip_interner[tree_node.handle]);
807
808        let has_blur = match &pictures[pic_index.0].composite_mode {
814            Some(PictureCompositeMode::Filter(Filter::Blur { .. })) => true,
815            Some(PictureCompositeMode::Filter(Filter::DropShadows { .. })) => true,
816            Some(PictureCompositeMode::SvgFilter( .. )) => true,
817            Some(PictureCompositeMode::SVGFEGraph( .. )) => true,
818            _ => false,
819        };
820
821        let direct_parent = lca_tree_node
827            .zip(pic_node_id)
828            .map(|(lca_tree_node, pic_node_id)| lca_tree_node.parent == pic_node_id)
829            .unwrap_or(false);
830
831        let should_set_clip_root = is_snapshot || lca_node.zip(pic_node).map_or(false, |(lca_node, pic_node)| {
832            lca_node.key == pic_node.key && !has_blur && direct_parent
841        });
842
843        if should_set_clip_root {
844            pictures[pic_index.0].clip_root = shared_clip_node_id;
845        }
846
847        for cluster in &prim_list.clusters {
849            for prim_instance_index in cluster.prim_range() {
850                if let PrimitiveInstanceKind::Picture { pic_index: child_pic_index, .. } = prim_instances[prim_instance_index].kind {
851                    let child_pic = &mut pictures[child_pic_index.0];
852
853                    if child_pic.spatial_node_index == SpatialNodeIndex::UNKNOWN {
854                        child_pic.spatial_node_index = spatial_node_index;
855                    }
856
857                    SceneBuilder::finalize_picture(
859                        child_pic_index,
860                        Some(prim_instance_index),
861                        pictures,
862                        Some(spatial_node_index),
863                        clip_tree_builder,
864                        prim_instances,
865                        clip_interner,
866                    );
867
868                    if pictures[child_pic_index.0].flags.contains(PictureFlags::DISABLE_SNAPPING) {
869                        pictures[pic_index.0].flags |= PictureFlags::DISABLE_SNAPPING;
870                    }
871                }
872            }
873        }
874
875        pictures[pic_index.0].prim_list = prim_list;
877    }
878
879    fn current_external_scroll_offset(
881        &mut self,
882        spatial_node_index: SpatialNodeIndex,
883    ) -> LayoutVector2D {
884        self.external_scroll_mapper
886            .external_scroll_offset(
887                spatial_node_index,
888                self.spatial_tree,
889            )
890    }
891
892    fn build_spatial_tree_for_display_list(
893        &mut self,
894        dl: &BuiltDisplayList,
895        pipeline_id: PipelineId,
896        instance_id: PipelineInstanceId,
897    ) {
898        dl.iter_spatial_tree(|item| {
899            match item {
900                SpatialTreeItem::ScrollFrame(descriptor) => {
901                    let parent_space = self.get_space(descriptor.parent_space);
902                    self.build_scroll_frame(
903                        descriptor,
904                        parent_space,
905                        pipeline_id,
906                        instance_id,
907                    );
908                }
909                SpatialTreeItem::ReferenceFrame(descriptor) => {
910                    let parent_space = self.get_space(descriptor.parent_spatial_id);
911                    self.build_reference_frame(
912                        descriptor,
913                        parent_space,
914                        pipeline_id,
915                        instance_id,
916                    );
917                }
918                SpatialTreeItem::StickyFrame(descriptor) => {
919                    let parent_space = self.get_space(descriptor.parent_spatial_id);
920                    self.build_sticky_frame(
921                        descriptor,
922                        parent_space,
923                        instance_id,
924                    );
925                }
926                SpatialTreeItem::Invalid => {
927                    unreachable!();
928                }
929            }
930        });
931    }
932
933    fn build_all(
934        &mut self,
935        root_pipeline_id: PipelineId,
936        root_pipeline: &ScenePipeline,
937    ) {
938        enum ContextKind<'a> {
939            Root,
940            StackingContext {
941                sc_info: StackingContextInfo,
942            },
943            ReferenceFrame,
944            Iframe {
945                parent_traversal: BuiltDisplayListIter<'a>,
946            }
947        }
948        struct BuildContext<'a> {
949            pipeline_id: PipelineId,
950            kind: ContextKind<'a>,
951        }
952
953        self.id_to_index_mapper_stack.push(NodeIdToIndexMapper::default());
954
955        let instance_id = self.get_next_instance_id_for_pipeline(root_pipeline_id);
956
957        self.push_root(
958            root_pipeline_id,
959            instance_id,
960        );
961        self.build_spatial_tree_for_display_list(
962            &root_pipeline.display_list.display_list,
963            root_pipeline_id,
964            instance_id,
965        );
966
967        let mut stack = vec![BuildContext {
968            pipeline_id: root_pipeline_id,
969            kind: ContextKind::Root,
970        }];
971        let mut traversal = root_pipeline.display_list.iter();
972
973        'outer: while let Some(bc) = stack.pop() {
974            loop {
975                let item = match traversal.next() {
976                    Some(item) => item,
977                    None => break,
978                };
979
980                match item.item() {
981                    DisplayItem::PushStackingContext(ref info) => {
982                        profile_scope!("build_stacking_context");
983                        let spatial_node_index = self.get_space(info.spatial_id);
984                        let mut subtraversal = item.sub_iter();
985                        if subtraversal.current_stacking_context_empty() && item.filters().is_empty() {
991                            subtraversal.skip_current_stacking_context();
992                            traversal = subtraversal;
993                            continue;
994                        }
995
996                        let snapshot = info.snapshot.map(|snapshot| {
997                            SnapshotInfo {
1001                                area: snapshot.area.translate(info.origin.to_vector()),
1002                                .. snapshot
1003                            }
1004                        });
1005
1006                        let composition_operations = CompositeOps::new(
1007                            filter_ops_for_compositing(item.filters()),
1008                            filter_datas_for_compositing(item.filter_datas()),
1009                            filter_primitives_for_compositing(item.filter_primitives()),
1010                            info.stacking_context.mix_blend_mode_for_compositing(),
1011                            snapshot,
1012                        );
1013
1014                        let sc_info = self.push_stacking_context(
1015                            composition_operations,
1016                            info.stacking_context.transform_style,
1017                            info.prim_flags,
1018                            spatial_node_index,
1019                            info.stacking_context.clip_chain_id,
1020                            info.stacking_context.raster_space,
1021                            info.stacking_context.flags,
1022                            info.ref_frame_offset + info.origin.to_vector(),
1023                        );
1024
1025                        let new_context = BuildContext {
1026                            pipeline_id: bc.pipeline_id,
1027                            kind: ContextKind::StackingContext {
1028                                sc_info,
1029                            },
1030                        };
1031                        stack.push(bc);
1032                        stack.push(new_context);
1033
1034                        subtraversal.merge_debug_stats_from(&mut traversal);
1035                        traversal = subtraversal;
1036                        continue 'outer;
1037                    }
1038                    DisplayItem::PushReferenceFrame(..) => {
1039                        profile_scope!("build_reference_frame");
1040                        let mut subtraversal = item.sub_iter();
1041
1042                        let new_context = BuildContext {
1043                            pipeline_id: bc.pipeline_id,
1044                            kind: ContextKind::ReferenceFrame,
1045                        };
1046                        stack.push(bc);
1047                        stack.push(new_context);
1048
1049                        subtraversal.merge_debug_stats_from(&mut traversal);
1050                        traversal = subtraversal;
1051                        continue 'outer;
1052                    }
1053                    DisplayItem::PopReferenceFrame |
1054                    DisplayItem::PopStackingContext => break,
1055                    DisplayItem::Iframe(ref info) => {
1056                        profile_scope!("iframe");
1057
1058                        let space = self.get_space(info.space_and_clip.spatial_id);
1059                        let subtraversal = match self.push_iframe(info, space) {
1060                            Some(pair) => pair,
1061                            None => continue,
1062                        };
1063
1064                        let new_context = BuildContext {
1065                            pipeline_id: info.pipeline_id,
1066                            kind: ContextKind::Iframe {
1067                                parent_traversal: mem::replace(&mut traversal, subtraversal),
1068                            },
1069                        };
1070                        stack.push(bc);
1071                        stack.push(new_context);
1072                        continue 'outer;
1073                    }
1074                    _ => {
1075                        self.build_item(item);
1076                    }
1077                };
1078            }
1079
1080            match bc.kind {
1081                ContextKind::Root => {}
1082                ContextKind::StackingContext { sc_info } => {
1083                    self.pop_stacking_context(sc_info);
1084                }
1085                ContextKind::ReferenceFrame => {
1086                }
1087                ContextKind::Iframe { parent_traversal } => {
1088                    self.iframe_size.pop();
1089                    self.clip_tree_builder.pop_clip();
1090                    self.clip_tree_builder.pop_clip();
1091
1092                    if self.iframe_size.is_empty() {
1093                        assert!(self.root_iframe_clip.is_some());
1094                        self.root_iframe_clip = None;
1095                        self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1096                    }
1097
1098                    self.id_to_index_mapper_stack.pop().unwrap();
1099
1100                    traversal = parent_traversal;
1101                }
1102            }
1103
1104            if cfg!(feature = "display_list_stats") {
1106                let stats = traversal.debug_stats();
1107                let total_bytes: usize = stats.iter().map(|(_, stats)| stats.num_bytes).sum();
1108                debug!("item, total count, total bytes, % of DL bytes, bytes per item");
1109                for (label, stats) in stats {
1110                    debug!("{}, {}, {}kb, {}%, {}",
1111                        label,
1112                        stats.total_count,
1113                        stats.num_bytes / 1000,
1114                        ((stats.num_bytes as f32 / total_bytes.max(1) as f32) * 100.0) as usize,
1115                        stats.num_bytes / stats.total_count.max(1));
1116                }
1117                debug!("");
1118            }
1119        }
1120
1121        debug_assert!(self.sc_stack.is_empty());
1122
1123        self.id_to_index_mapper_stack.pop().unwrap();
1124        assert!(self.id_to_index_mapper_stack.is_empty());
1125    }
1126
1127    fn build_sticky_frame(
1128        &mut self,
1129        info: &StickyFrameDescriptor,
1130        parent_node_index: SpatialNodeIndex,
1131        instance_id: PipelineInstanceId,
1132    ) {
1133        let external_scroll_offset = self.current_external_scroll_offset(parent_node_index);
1134
1135        let sticky_frame_info = StickyFrameInfo::new(
1136            info.bounds.translate(external_scroll_offset),
1137            info.margins,
1138            info.vertical_offset_bounds,
1139            info.horizontal_offset_bounds,
1140            info.previously_applied_offset,
1141            info.transform,
1142        );
1143
1144        let index = self.spatial_tree.add_sticky_frame(
1145            parent_node_index,
1146            sticky_frame_info,
1147            info.id.pipeline_id(),
1148            info.key,
1149            instance_id,
1150        );
1151        self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(info.id, index);
1152    }
1153
1154    fn build_reference_frame(
1155        &mut self,
1156        info: &ReferenceFrameDescriptor,
1157        parent_space: SpatialNodeIndex,
1158        pipeline_id: PipelineId,
1159        instance_id: PipelineInstanceId,
1160    ) {
1161        let transform = match info.reference_frame.transform {
1162            ReferenceTransformBinding::Static { binding } => binding,
1163            ReferenceTransformBinding::Computed { scale_from, vertical_flip, rotation } => {
1164                let content_size = &self.iframe_size.last().unwrap();
1165
1166                let mut transform = if let Some(scale_from) = scale_from {
1167                    match rotation {
1171                        Rotation::Degree0 |
1172                        Rotation::Degree180 => {
1173                            LayoutTransform::scale(
1174                                content_size.width / scale_from.width,
1175                                content_size.height / scale_from.height,
1176                                1.0
1177                            )
1178                        },
1179                        Rotation::Degree90 |
1180                        Rotation::Degree270 => {
1181                            LayoutTransform::scale(
1182                                content_size.height / scale_from.width,
1183                                content_size.width / scale_from.height,
1184                                1.0
1185                            )
1186
1187                        }
1188                    }
1189                } else {
1190                    LayoutTransform::identity()
1191                };
1192
1193                if vertical_flip {
1194                    let content_size = &self.iframe_size.last().unwrap();
1195                    let content_height = match rotation {
1196                        Rotation::Degree0 | Rotation::Degree180 => content_size.height,
1197                        Rotation::Degree90 | Rotation::Degree270 => content_size.width,
1198                    };
1199                    transform = transform
1200                        .then_translate(LayoutVector3D::new(0.0, content_height, 0.0))
1201                        .pre_scale(1.0, -1.0, 1.0);
1202                }
1203
1204                let rotate = rotation.to_matrix(**content_size);
1205                let transform = transform.then(&rotate);
1206
1207                PropertyBinding::Value(transform)
1208            },
1209        };
1210
1211        let external_scroll_offset = self.current_external_scroll_offset(parent_space);
1212
1213        self.push_reference_frame(
1214            info.reference_frame.id,
1215            parent_space,
1216            pipeline_id,
1217            info.reference_frame.transform_style,
1218            transform,
1219            info.reference_frame.kind,
1220            (info.origin + external_scroll_offset).to_vector(),
1221            SpatialNodeUid::external(info.reference_frame.key, pipeline_id, instance_id),
1222        );
1223    }
1224
1225    fn build_scroll_frame(
1226        &mut self,
1227        info: &ScrollFrameDescriptor,
1228        parent_node_index: SpatialNodeIndex,
1229        pipeline_id: PipelineId,
1230        instance_id: PipelineInstanceId,
1231    ) {
1232        let content_size = info.content_rect.size();
1236        let external_scroll_offset = self.current_external_scroll_offset(parent_node_index);
1237
1238        self.add_scroll_frame(
1239            info.scroll_frame_id,
1240            parent_node_index,
1241            info.external_id,
1242            pipeline_id,
1243            &info.frame_rect.translate(external_scroll_offset),
1244            &content_size,
1245            ScrollFrameKind::Explicit,
1246            info.external_scroll_offset,
1247            info.scroll_offset_generation,
1248            info.has_scroll_linked_effect,
1249            SpatialNodeUid::external(info.key, pipeline_id, instance_id),
1250        );
1251    }
1252
1253    fn get_next_instance_id_for_pipeline(
1255        &mut self,
1256        pipeline_id: PipelineId,
1257    ) -> PipelineInstanceId {
1258        let next_instance = self.pipeline_instance_ids
1259            .entry(pipeline_id)
1260            .or_insert(0);
1261
1262        let instance_id = PipelineInstanceId::new(*next_instance);
1263        *next_instance += 1;
1264
1265        instance_id
1266    }
1267
1268    fn push_iframe(
1269        &mut self,
1270        info: &IframeDisplayItem,
1271        spatial_node_index: SpatialNodeIndex,
1272    ) -> Option<BuiltDisplayListIter<'a>> {
1273        let iframe_pipeline_id = info.pipeline_id;
1274        let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
1275            Some(pipeline) => pipeline,
1276            None => {
1277                debug_assert!(info.ignore_missing_pipeline);
1278                return None
1279            },
1280        };
1281
1282        self.clip_tree_builder.push_clip_chain(Some(info.space_and_clip.clip_chain_id), false, false);
1283
1284        self.add_rect_clip_node(
1286            ClipId::root(iframe_pipeline_id),
1287            info.space_and_clip.spatial_id,
1288            &info.clip_rect,
1289        );
1290
1291        self.clip_tree_builder.push_clip_id(ClipId::root(iframe_pipeline_id));
1292
1293        let instance_id = self.get_next_instance_id_for_pipeline(iframe_pipeline_id);
1294
1295        self.id_to_index_mapper_stack.push(NodeIdToIndexMapper::default());
1296
1297        let bounds = self.normalize_scroll_offset_and_snap_rect(
1298            &info.bounds,
1299            spatial_node_index,
1300        );
1301
1302        let spatial_node_index = self.push_reference_frame(
1303            SpatialId::root_reference_frame(iframe_pipeline_id),
1304            spatial_node_index,
1305            iframe_pipeline_id,
1306            TransformStyle::Flat,
1307            PropertyBinding::Value(LayoutTransform::identity()),
1308            ReferenceFrameKind::Transform {
1309                is_2d_scale_translation: true,
1310                should_snap: true,
1311                paired_with_perspective: false,
1312            },
1313            bounds.min.to_vector(),
1314            SpatialNodeUid::root_reference_frame(iframe_pipeline_id, instance_id),
1315        );
1316
1317        let iframe_rect = LayoutRect::from_size(bounds.size());
1318        let is_root_pipeline = self.iframe_size.is_empty();
1319
1320        self.add_scroll_frame(
1321            SpatialId::root_scroll_node(iframe_pipeline_id),
1322            spatial_node_index,
1323            ExternalScrollId(0, iframe_pipeline_id),
1324            iframe_pipeline_id,
1325            &iframe_rect,
1326            &bounds.size(),
1327            ScrollFrameKind::PipelineRoot {
1328                is_root_pipeline,
1329            },
1330            LayoutVector2D::zero(),
1331            APZScrollGeneration::default(),
1332            HasScrollLinkedEffect::No,
1333            SpatialNodeUid::root_scroll_frame(iframe_pipeline_id, instance_id),
1334        );
1335
1336        if self.iframe_size.is_empty() {
1339            assert!(self.root_iframe_clip.is_none());
1340            self.root_iframe_clip = Some(ClipId::root(iframe_pipeline_id));
1341            self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1342        }
1343        self.iframe_size.push(bounds.size());
1344
1345        self.build_spatial_tree_for_display_list(
1346            &pipeline.display_list.display_list,
1347            iframe_pipeline_id,
1348            instance_id,
1349        );
1350
1351        Some(pipeline.display_list.iter())
1352    }
1353
1354    fn get_space(
1355        &self,
1356        spatial_id: SpatialId,
1357    ) -> SpatialNodeIndex {
1358        self.id_to_index_mapper_stack.last().unwrap().get_spatial_node_index(spatial_id)
1359    }
1360
1361    fn get_clip_node(
1362        &mut self,
1363        clip_chain_id: api::ClipChainId,
1364    ) -> ClipNodeId {
1365        self.clip_tree_builder.build_clip_set(
1366            clip_chain_id,
1367        )
1368    }
1369
1370    fn process_common_properties(
1371        &mut self,
1372        common: &CommonItemProperties,
1373        bounds: Option<LayoutRect>,
1374    ) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipNodeId) {
1375        let spatial_node_index = self.get_space(common.spatial_id);
1376
1377        let mut clip_rect = common.clip_rect;
1379        let mut prim_rect = bounds.unwrap_or(clip_rect);
1380        let unsnapped_rect = self.normalize_rect_scroll_offset(&prim_rect, spatial_node_index);
1381
1382        if common.flags.contains(PrimitiveFlags::ANTIALISED) {
1387            prim_rect = self.normalize_rect_scroll_offset(&prim_rect, spatial_node_index);
1388            clip_rect = self.normalize_rect_scroll_offset(&clip_rect, spatial_node_index);
1389        } else {
1390            clip_rect = self.normalize_scroll_offset_and_snap_rect(
1391                &clip_rect,
1392                spatial_node_index,
1393            );
1394
1395            prim_rect = self.normalize_scroll_offset_and_snap_rect(
1396                &prim_rect,
1397                spatial_node_index,
1398            );
1399        }
1400
1401        let clip_node_id = self.get_clip_node(
1402            common.clip_chain_id,
1403        );
1404
1405        let layout = LayoutPrimitiveInfo {
1406            rect: prim_rect,
1407            clip_rect,
1408            flags: common.flags,
1409        };
1410
1411        (layout, unsnapped_rect, spatial_node_index, clip_node_id)
1412    }
1413
1414    fn process_common_properties_with_bounds(
1415        &mut self,
1416        common: &CommonItemProperties,
1417        bounds: LayoutRect,
1418    ) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipNodeId) {
1419        self.process_common_properties(
1420            common,
1421            Some(bounds),
1422        )
1423    }
1424
1425    fn normalize_rect_scroll_offset(
1429        &mut self,
1430        rect: &LayoutRect,
1431        spatial_node_index: SpatialNodeIndex,
1432    ) -> LayoutRect {
1433        let current_offset = self.current_external_scroll_offset(spatial_node_index);
1434
1435        rect.translate(current_offset)
1436    }
1437
1438    fn normalize_scroll_offset_and_snap_rect(
1442        &mut self,
1443        rect: &LayoutRect,
1444        target_spatial_node: SpatialNodeIndex,
1445    ) -> LayoutRect {
1446        let rect = self.normalize_rect_scroll_offset(rect, target_spatial_node);
1447
1448        self.snap_to_device.set_target_spatial_node(
1449            target_spatial_node,
1450            self.spatial_tree,
1451        );
1452        self.snap_to_device.snap_rect(&rect)
1453    }
1454
1455    fn build_item<'b>(
1456        &'b mut self,
1457        item: DisplayItemRef,
1458    ) {
1459        match *item.item() {
1460            DisplayItem::Image(ref info) => {
1461                profile_scope!("image");
1462
1463                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1464                    &info.common,
1465                    info.bounds,
1466                );
1467
1468                self.add_image(
1469                    spatial_node_index,
1470                    clip_node_id,
1471                    &layout,
1472                    layout.rect.size(),
1473                    LayoutSize::zero(),
1474                    info.image_key,
1475                    info.image_rendering,
1476                    info.alpha_type,
1477                    info.color,
1478                );
1479            }
1480            DisplayItem::RepeatingImage(ref info) => {
1481                profile_scope!("repeating_image");
1482
1483                let (layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1484                    &info.common,
1485                    info.bounds,
1486                );
1487
1488                let stretch_size = process_repeat_size(
1489                    &layout.rect,
1490                    &unsnapped_rect,
1491                    info.stretch_size,
1492                );
1493
1494                self.add_image(
1495                    spatial_node_index,
1496                    clip_node_id,
1497                    &layout,
1498                    stretch_size,
1499                    info.tile_spacing,
1500                    info.image_key,
1501                    info.image_rendering,
1502                    info.alpha_type,
1503                    info.color,
1504                );
1505            }
1506            DisplayItem::YuvImage(ref info) => {
1507                profile_scope!("yuv_image");
1508
1509                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1510                    &info.common,
1511                    info.bounds,
1512                );
1513
1514                self.add_yuv_image(
1515                    spatial_node_index,
1516                    clip_node_id,
1517                    &layout,
1518                    info.yuv_data,
1519                    info.color_depth,
1520                    info.color_space,
1521                    info.color_range,
1522                    info.image_rendering,
1523                );
1524            }
1525            DisplayItem::Text(ref info) => {
1526                profile_scope!("text");
1527
1528                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1535                    &info.common,
1536                    info.bounds,
1537                );
1538
1539                self.add_text(
1540                    spatial_node_index,
1541                    clip_node_id,
1542                    &layout,
1543                    &info.font_key,
1544                    &info.color,
1545                    item.glyphs(),
1546                    info.glyph_options,
1547                    info.ref_frame_offset,
1548                );
1549            }
1550            DisplayItem::Rectangle(ref info) => {
1551                profile_scope!("rect");
1552
1553                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1554                    &info.common,
1555                    info.bounds,
1556                );
1557
1558                self.add_primitive(
1559                    spatial_node_index,
1560                    clip_node_id,
1561                    &layout,
1562                    Vec::new(),
1563                    PrimitiveKeyKind::Rectangle {
1564                        color: info.color.into(),
1565                    },
1566                );
1567
1568                if info.common.flags.contains(PrimitiveFlags::CHECKERBOARD_BACKGROUND) {
1569                    self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1570                }
1571            }
1572            DisplayItem::HitTest(ref info) => {
1573                profile_scope!("hit_test");
1574
1575                let spatial_node_index = self.get_space(info.spatial_id);
1576
1577                let rect = self.normalize_scroll_offset_and_snap_rect(
1578                    &info.rect,
1579                    spatial_node_index,
1580                );
1581
1582                let layout = LayoutPrimitiveInfo {
1583                    rect,
1584                    clip_rect: rect,
1585                    flags: info.flags,
1586                };
1587
1588                let spatial_node = self.spatial_tree.get_node_info(spatial_node_index);
1589                let anim_id: u64 =  match spatial_node.node_type {
1590                    SpatialNodeType::ReferenceFrame(ReferenceFrameInfo {
1591                        source_transform: PropertyBinding::Binding(key, _),
1592                        ..
1593                    }) => key.clone().into(),
1594                    SpatialNodeType::StickyFrame(StickyFrameInfo {
1595                        transform: Some(PropertyBinding::Binding(key, _)),
1596                        ..
1597                    }) => key.clone().into(),
1598                    _ => 0,
1599                };
1600
1601                let clip_node_id = self.get_clip_node(info.clip_chain_id);
1602
1603                self.add_primitive_to_hit_testing_list(
1604                    &layout,
1605                    spatial_node_index,
1606                    clip_node_id,
1607                    info.tag,
1608                    anim_id,
1609                );
1610            }
1611            DisplayItem::ClearRectangle(ref info) => {
1612                profile_scope!("clear");
1613
1614                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1615                    &info.common,
1616                    info.bounds,
1617                );
1618
1619                self.add_clear_rectangle(
1620                    spatial_node_index,
1621                    clip_node_id,
1622                    &layout,
1623                );
1624            }
1625            DisplayItem::Line(ref info) => {
1626                profile_scope!("line");
1627
1628                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1629                    &info.common,
1630                    info.area,
1631                );
1632
1633                self.add_line(
1634                    spatial_node_index,
1635                    clip_node_id,
1636                    &layout,
1637                    info.wavy_line_thickness,
1638                    info.orientation,
1639                    info.color,
1640                    info.style,
1641                );
1642            }
1643            DisplayItem::Gradient(ref info) => {
1644                profile_scope!("gradient");
1645
1646                if !info.gradient.is_valid() {
1647                    return;
1648                }
1649
1650                let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1651                    &info.common,
1652                    info.bounds,
1653                );
1654
1655                let mut tile_size = process_repeat_size(
1656                    &layout.rect,
1657                    &unsnapped_rect,
1658                    info.tile_size,
1659                );
1660
1661                let mut stops = read_gradient_stops(item.gradient_stops());
1662                let mut start = info.gradient.start_point;
1663                let mut end = info.gradient.end_point;
1664                let flags = layout.flags;
1665
1666                let optimized = optimize_linear_gradient(
1667                    &mut layout.rect,
1668                    &mut tile_size,
1669                    info.tile_spacing,
1670                    &layout.clip_rect,
1671                    &mut start,
1672                    &mut end,
1673                    info.gradient.extend_mode,
1674                    &mut stops,
1675                    self.config.enable_dithering,
1676                    &mut |rect, start, end, stops, edge_aa_mask| {
1677                        let layout = LayoutPrimitiveInfo { rect: *rect, clip_rect: *rect, flags };
1678                        if let Some(prim_key_kind) = self.create_linear_gradient_prim(
1679                            &layout,
1680                            start,
1681                            end,
1682                            stops.to_vec(),
1683                            ExtendMode::Clamp,
1684                            rect.size(),
1685                            LayoutSize::zero(),
1686                            None,
1687                            edge_aa_mask,
1688                        ) {
1689                            self.add_nonshadowable_primitive(
1690                                spatial_node_index,
1691                                clip_node_id,
1692                                &layout,
1693                                Vec::new(),
1694                                prim_key_kind,
1695                            );
1696                        }
1697                    }
1698                );
1699
1700                if !optimized && !tile_size.ceil().is_empty() {
1701                    if let Some(prim_key_kind) = self.create_linear_gradient_prim(
1702                        &layout,
1703                        start,
1704                        end,
1705                        stops,
1706                        info.gradient.extend_mode,
1707                        tile_size,
1708                        info.tile_spacing,
1709                        None,
1710                        EdgeAaSegmentMask::all(),
1711                    ) {
1712                        self.add_nonshadowable_primitive(
1713                            spatial_node_index,
1714                            clip_node_id,
1715                            &layout,
1716                            Vec::new(),
1717                            prim_key_kind,
1718                        );
1719                    }
1720                }
1721            }
1722            DisplayItem::RadialGradient(ref info) => {
1723                profile_scope!("radial");
1724
1725                if !info.gradient.is_valid() {
1726                    return;
1727                }
1728
1729                let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1730                    &info.common,
1731                    info.bounds,
1732                );
1733
1734                let mut center = info.gradient.center;
1735
1736                let stops = read_gradient_stops(item.gradient_stops());
1737
1738                let mut tile_size = process_repeat_size(
1739                    &layout.rect,
1740                    &unsnapped_rect,
1741                    info.tile_size,
1742                );
1743
1744                let mut prim_rect = layout.rect;
1745                let mut tile_spacing = info.tile_spacing;
1746                optimize_radial_gradient(
1747                    &mut prim_rect,
1748                    &mut tile_size,
1749                    &mut center,
1750                    &mut tile_spacing,
1751                    &layout.clip_rect,
1752                    info.gradient.radius,
1753                    info.gradient.end_offset,
1754                    info.gradient.extend_mode,
1755                    &stops,
1756                    &mut |solid_rect, color| {
1757                        self.add_nonshadowable_primitive(
1758                            spatial_node_index,
1759                            clip_node_id,
1760                            &LayoutPrimitiveInfo {
1761                                rect: *solid_rect,
1762                                .. layout
1763                            },
1764                            Vec::new(),
1765                            PrimitiveKeyKind::Rectangle { color: PropertyBinding::Value(color) },
1766                        );
1767                    }
1768                );
1769
1770                simplify_repeated_primitive(&tile_size, &mut tile_spacing, &mut prim_rect);
1775
1776                if !tile_size.ceil().is_empty() {
1777                    layout.rect = prim_rect;
1778                    let prim_key_kind = self.create_radial_gradient_prim(
1779                        &layout,
1780                        center,
1781                        info.gradient.start_offset * info.gradient.radius.width,
1782                        info.gradient.end_offset * info.gradient.radius.width,
1783                        info.gradient.radius.width / info.gradient.radius.height,
1784                        stops,
1785                        info.gradient.extend_mode,
1786                        tile_size,
1787                        tile_spacing,
1788                        None,
1789                    );
1790
1791                    self.add_nonshadowable_primitive(
1792                        spatial_node_index,
1793                        clip_node_id,
1794                        &layout,
1795                        Vec::new(),
1796                        prim_key_kind,
1797                    );
1798                }
1799            }
1800            DisplayItem::ConicGradient(ref info) => {
1801                profile_scope!("conic");
1802
1803                if !info.gradient.is_valid() {
1804                    return;
1805                }
1806
1807                let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1808                    &info.common,
1809                    info.bounds,
1810                );
1811
1812                let tile_size = process_repeat_size(
1813                    &layout.rect,
1814                    &unsnapped_rect,
1815                    info.tile_size,
1816                );
1817
1818                let offset = apply_gradient_local_clip(
1819                    &mut layout.rect,
1820                    &tile_size,
1821                    &info.tile_spacing,
1822                    &layout.clip_rect,
1823                );
1824                let center = info.gradient.center + offset;
1825
1826                if !tile_size.ceil().is_empty() {
1827                    let prim_key_kind = self.create_conic_gradient_prim(
1828                        &layout,
1829                        center,
1830                        info.gradient.angle,
1831                        info.gradient.start_offset,
1832                        info.gradient.end_offset,
1833                        item.gradient_stops(),
1834                        info.gradient.extend_mode,
1835                        tile_size,
1836                        info.tile_spacing,
1837                        None,
1838                    );
1839
1840                    self.add_nonshadowable_primitive(
1841                        spatial_node_index,
1842                        clip_node_id,
1843                        &layout,
1844                        Vec::new(),
1845                        prim_key_kind,
1846                    );
1847                }
1848            }
1849            DisplayItem::BoxShadow(ref info) => {
1850                profile_scope!("box_shadow");
1851
1852                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1853                    &info.common,
1854                    info.box_bounds,
1855                );
1856
1857                self.add_box_shadow(
1858                    spatial_node_index,
1859                    clip_node_id,
1860                    &layout,
1861                    &info.offset,
1862                    info.color,
1863                    info.blur_radius,
1864                    info.spread_radius,
1865                    info.border_radius,
1866                    info.clip_mode,
1867                    self.spatial_tree.is_root_coord_system(spatial_node_index),
1868                );
1869            }
1870            DisplayItem::Border(ref info) => {
1871                profile_scope!("border");
1872
1873                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1874                    &info.common,
1875                    info.bounds,
1876                );
1877
1878                self.add_border(
1879                    spatial_node_index,
1880                    clip_node_id,
1881                    &layout,
1882                    info,
1883                    item.gradient_stops(),
1884                );
1885            }
1886            DisplayItem::ImageMaskClip(ref info) => {
1887                profile_scope!("image_clip");
1888
1889                self.add_image_mask_clip_node(
1890                    info.id,
1891                    info.spatial_id,
1892                    &info.image_mask,
1893                    info.fill_rule,
1894                    item.points(),
1895                );
1896            }
1897            DisplayItem::RoundedRectClip(ref info) => {
1898                profile_scope!("rounded_clip");
1899
1900                self.add_rounded_rect_clip_node(
1901                    info.id,
1902                    info.spatial_id,
1903                    &info.clip,
1904                );
1905            }
1906            DisplayItem::RectClip(ref info) => {
1907                profile_scope!("rect_clip");
1908
1909                self.add_rect_clip_node(
1910                    info.id,
1911                    info.spatial_id,
1912                    &info.clip_rect,
1913                );
1914            }
1915            DisplayItem::ClipChain(ref info) => {
1916                profile_scope!("clip_chain");
1917
1918                self.clip_tree_builder.define_clip_chain(
1919                    info.id,
1920                    info.parent,
1921                    item.clip_chain_items().into_iter(),
1922                );
1923            },
1924            DisplayItem::BackdropFilter(ref info) => {
1925                profile_scope!("backdrop");
1926
1927                let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties(
1928                    &info.common,
1929                    None,
1930                );
1931
1932                let filters = filter_ops_for_compositing(item.filters());
1933                let filter_datas = filter_datas_for_compositing(item.filter_datas());
1934                let filter_primitives = filter_primitives_for_compositing(item.filter_primitives());
1935
1936                self.add_backdrop_filter(
1937                    spatial_node_index,
1938                    clip_node_id,
1939                    &layout,
1940                    filters,
1941                    filter_datas,
1942                    filter_primitives,
1943                );
1944            }
1945
1946            DisplayItem::SetGradientStops |
1948            DisplayItem::SetFilterOps |
1949            DisplayItem::SetFilterData |
1950            DisplayItem::SetFilterPrimitives |
1951            DisplayItem::SetPoints => {}
1952
1953            DisplayItem::PushStackingContext(..) |
1955            DisplayItem::PushReferenceFrame(..) |
1956            DisplayItem::PopReferenceFrame |
1957            DisplayItem::PopStackingContext |
1958            DisplayItem::Iframe(_) => {
1959                unreachable!("Handled in `build_all`")
1960            }
1961
1962            DisplayItem::ReuseItems(key) |
1963            DisplayItem::RetainedItems(key) => {
1964                unreachable!("Iterator logic error: {:?}", key);
1965            }
1966
1967            DisplayItem::PushShadow(info) => {
1968                profile_scope!("push_shadow");
1969
1970                let spatial_node_index = self.get_space(info.space_and_clip.spatial_id);
1971
1972                self.push_shadow(
1973                    info.shadow,
1974                    spatial_node_index,
1975                    info.space_and_clip.clip_chain_id,
1976                    info.should_inflate,
1977                );
1978            }
1979            DisplayItem::PopAllShadows => {
1980                profile_scope!("pop_all_shadows");
1981
1982                self.pop_all_shadows();
1983            }
1984            DisplayItem::DebugMarker(..) => {}
1985        }
1986    }
1987
1988    fn create_primitive<P>(
1994        &mut self,
1995        info: &LayoutPrimitiveInfo,
1996        clip_leaf_id: ClipLeafId,
1997        prim: P,
1998    ) -> PrimitiveInstance
1999    where
2000        P: InternablePrimitive,
2001        Interners: AsMut<Interner<P>>,
2002    {
2003        let prim_key = prim.into_key(info);
2005
2006        let interner = self.interners.as_mut();
2007        let prim_data_handle = interner
2008            .intern(&prim_key, || ());
2009
2010        let instance_kind = P::make_instance_kind(
2011            prim_key,
2012            prim_data_handle,
2013            &mut self.prim_store,
2014        );
2015
2016        PrimitiveInstance::new(
2017            instance_kind,
2018            clip_leaf_id,
2019        )
2020    }
2021
2022    fn add_primitive_to_hit_testing_list(
2023        &mut self,
2024        info: &LayoutPrimitiveInfo,
2025        spatial_node_index: SpatialNodeIndex,
2026        clip_node_id: ClipNodeId,
2027        tag: ItemTag,
2028        anim_id: u64,
2029    ) {
2030        self.hit_testing_scene.add_item(
2031            tag,
2032            anim_id,
2033            info,
2034            spatial_node_index,
2035            clip_node_id,
2036            &self.clip_tree_builder,
2037            self.interners,
2038        );
2039    }
2040
2041    pub fn add_primitive_to_draw_list(
2043        &mut self,
2044        prim_instance: PrimitiveInstance,
2045        prim_rect: LayoutRect,
2046        spatial_node_index: SpatialNodeIndex,
2047        flags: PrimitiveFlags,
2048    ) {
2049        match self.sc_stack.last_mut() {
2055            Some(stacking_context) => {
2056                stacking_context.prim_list.add_prim(
2057                    prim_instance,
2058                    prim_rect,
2059                    spatial_node_index,
2060                    flags,
2061                    &mut self.prim_instances,
2062                    &self.clip_tree_builder,
2063                );
2064            }
2065            None => {
2066                self.tile_cache_builder.add_prim(
2067                    prim_instance,
2068                    prim_rect,
2069                    spatial_node_index,
2070                    flags,
2071                    self.spatial_tree,
2072                    self.interners,
2073                    &self.quality_settings,
2074                    &mut self.prim_instances,
2075                    &self.clip_tree_builder,
2076                );
2077            }
2078        }
2079    }
2080
2081    pub fn add_nonshadowable_primitive<P>(
2084        &mut self,
2085        spatial_node_index: SpatialNodeIndex,
2086        clip_node_id: ClipNodeId,
2087        info: &LayoutPrimitiveInfo,
2088        clip_items: Vec<ClipItemKey>,
2089        prim: P,
2090    )
2091    where
2092        P: InternablePrimitive + IsVisible,
2093        Interners: AsMut<Interner<P>>,
2094    {
2095        if prim.is_visible() {
2096            let clip_leaf_id = self.clip_tree_builder.build_for_prim(
2097                clip_node_id,
2098                info,
2099                &clip_items,
2100                &mut self.interners,
2101            );
2102
2103            self.add_prim_to_draw_list(
2104                info,
2105                spatial_node_index,
2106                clip_leaf_id,
2107                prim,
2108            );
2109        }
2110    }
2111
2112    pub fn add_primitive<P>(
2113        &mut self,
2114        spatial_node_index: SpatialNodeIndex,
2115        clip_node_id: ClipNodeId,
2116        info: &LayoutPrimitiveInfo,
2117        clip_items: Vec<ClipItemKey>,
2118        prim: P,
2119    )
2120    where
2121        P: InternablePrimitive + IsVisible,
2122        Interners: AsMut<Interner<P>>,
2123        ShadowItem: From<PendingPrimitive<P>>
2124    {
2125        if self.pending_shadow_items.is_empty() {
2128            self.add_nonshadowable_primitive(
2129                spatial_node_index,
2130                clip_node_id,
2131                info,
2132                clip_items,
2133                prim,
2134            );
2135        } else {
2136            debug_assert!(clip_items.is_empty(), "No per-prim clips expected for shadowed primitives");
2137
2138            self.pending_shadow_items.push_back(PendingPrimitive {
2141                spatial_node_index,
2142                clip_node_id,
2143                info: *info,
2144                prim,
2145            }.into());
2146        }
2147    }
2148
2149    fn add_prim_to_draw_list<P>(
2150        &mut self,
2151        info: &LayoutPrimitiveInfo,
2152        spatial_node_index: SpatialNodeIndex,
2153        clip_leaf_id: ClipLeafId,
2154        prim: P,
2155    )
2156    where
2157        P: InternablePrimitive,
2158        Interners: AsMut<Interner<P>>,
2159    {
2160        let prim_instance = self.create_primitive(
2161            info,
2162            clip_leaf_id,
2163            prim,
2164        );
2165        self.add_primitive_to_draw_list(
2166            prim_instance,
2167            info.rect,
2168            spatial_node_index,
2169            info.flags,
2170        );
2171    }
2172
2173    fn make_current_slice_atomic_if_required(&mut self) {
2174        let has_non_wrapping_sc = self.sc_stack
2175            .iter()
2176            .position(|sc| {
2177                !sc.flags.contains(StackingContextFlags::WRAPS_BACKDROP_FILTER)
2178            })
2179            .is_some();
2180
2181        if has_non_wrapping_sc {
2182            return;
2183        }
2184
2185        assert!(self.pending_shadow_items.is_empty());
2187        self.tile_cache_builder.make_current_slice_atomic();
2188    }
2189
2190    fn add_tile_cache_barrier_if_needed(
2193        &mut self,
2194        slice_flags: SliceFlags,
2195    ) {
2196        if self.sc_stack.is_empty() {
2197            assert!(self.pending_shadow_items.is_empty());
2199
2200            self.tile_cache_builder.add_tile_cache_barrier(
2201                slice_flags,
2202                self.root_iframe_clip,
2203            );
2204        }
2205    }
2206
2207    fn push_stacking_context(
2209        &mut self,
2210        mut composite_ops: CompositeOps,
2211        transform_style: TransformStyle,
2212        prim_flags: PrimitiveFlags,
2213        spatial_node_index: SpatialNodeIndex,
2214        clip_chain_id: Option<api::ClipChainId>,
2215        requested_raster_space: RasterSpace,
2216        flags: StackingContextFlags,
2217        subregion_offset: LayoutVector2D,
2218    ) -> StackingContextInfo {
2219        profile_scope!("push_stacking_context");
2220
2221        let needs_extra_stacking_context = composite_ops.snapshot.is_some()
2230            && composite_ops.has_valid_filters();
2231
2232        if needs_extra_stacking_context {
2233            let snapshot = mem::take(&mut composite_ops.snapshot);
2234            let mut info = self.push_stacking_context(
2235                CompositeOps {
2236                    filters: Vec::new(),
2237                    filter_datas: Vec::new(),
2238                    filter_primitives: Vec::new(),
2239                    mix_blend_mode: None,
2240                    snapshot,
2241                },
2242                TransformStyle::Flat,
2243                prim_flags,
2244                spatial_node_index,
2245                clip_chain_id,
2246                requested_raster_space,
2247                flags,
2248                LayoutVector2D::zero(),
2249            );
2250            info.pop_stacking_context = true;
2251            self.extra_stacking_context_stack.push(info);
2252        }
2253
2254        let clip_node_id = match clip_chain_id {
2255            Some(id) => {
2256                self.clip_tree_builder.build_clip_set(id)
2257            }
2258            None => {
2259                self.clip_tree_builder.build_clip_set(api::ClipChainId::INVALID)
2260            }
2261        };
2262
2263        self.clip_tree_builder.push_clip_chain(
2264            clip_chain_id,
2265            !composite_ops.is_empty(),
2266            composite_ops.snapshot.is_some(),
2267        );
2268
2269        let new_space = match (self.raster_space_stack.last(), requested_raster_space) {
2270            (None, _) => requested_raster_space,
2272            (Some(parent_space), RasterSpace::Screen) => *parent_space,
2274            (Some(RasterSpace::Screen), space) => space,
2276            (Some(RasterSpace::Local(parent_scale)), RasterSpace::Local(scale)) => RasterSpace::Local(parent_scale.max(scale)),
2278        };
2279        self.raster_space_stack.push(new_space);
2280
2281        let (parent_is_3d, extra_3d_instance, plane_splitter_index) = match self.sc_stack.last_mut() {
2285            Some(ref mut sc) if sc.is_3d() => {
2286                let (flat_items_context_3d, plane_splitter_index) = match sc.context_3d {
2287                    Picture3DContext::In { ancestor_index, plane_splitter_index, .. } => {
2288                        (
2289                            Picture3DContext::In {
2290                                root_data: None,
2291                                ancestor_index,
2292                                plane_splitter_index,
2293                            },
2294                            plane_splitter_index,
2295                        )
2296                    }
2297                    Picture3DContext::Out => panic!("Unexpected out of 3D context"),
2298                };
2299                let extra_instance = sc.cut_item_sequence(
2302                    &mut self.prim_store,
2303                    &mut self.interners,
2304                    Some(PictureCompositeMode::Blit(BlitReason::PRESERVE3D)),
2305                    flat_items_context_3d,
2306                    &mut self.clip_tree_builder,
2307                );
2308                let extra_instance = extra_instance.map(|(_, instance)| {
2309                    ExtendedPrimitiveInstance {
2310                        instance,
2311                        spatial_node_index: sc.spatial_node_index,
2312                        flags: sc.prim_flags,
2313                    }
2314                });
2315                (true, extra_instance, Some(plane_splitter_index))
2316            },
2317            _ => (false, None, None),
2318        };
2319
2320        if let Some(instance) = extra_3d_instance {
2321            self.add_primitive_instance_to_3d_root(instance);
2322        }
2323
2324        let participating_in_3d_context =
2330            composite_ops.is_empty() &&
2331            (parent_is_3d || transform_style == TransformStyle::Preserve3D);
2332
2333        let context_3d = if participating_in_3d_context {
2334            let ancestor_index = self.containing_block_stack
2337                .last()
2338                .cloned()
2339                .unwrap_or(self.spatial_tree.root_reference_frame_index());
2340
2341            let plane_splitter_index = plane_splitter_index.unwrap_or_else(|| {
2342                let index = self.next_plane_splitter_index;
2343                self.next_plane_splitter_index += 1;
2344                PlaneSplitterIndex(index)
2345            });
2346
2347            Picture3DContext::In {
2348                root_data: if parent_is_3d {
2349                    None
2350                } else {
2351                    Some(Vec::new())
2352                },
2353                plane_splitter_index,
2354                ancestor_index,
2355            }
2356        } else {
2357            Picture3DContext::Out
2358        };
2359
2360        let mut blit_reason = BlitReason::empty();
2365
2366        if flags.contains(StackingContextFlags::FORCED_ISOLATION) {
2369            blit_reason = BlitReason::FORCED_ISOLATION;
2370        }
2371
2372        if composite_ops.snapshot.is_some() {
2374            blit_reason = BlitReason::SNAPSHOT;
2375        }
2376
2377        if let Some(clip_chain_id) = clip_chain_id {
2380            if self.clip_tree_builder.clip_chain_has_complex_clips(clip_chain_id, &self.interners) {
2381                blit_reason |= BlitReason::CLIP;
2382            }
2383        }
2384
2385        let mut is_redundant = FlattenedStackingContext::is_redundant(
2388            &context_3d,
2389            &composite_ops,
2390            blit_reason,
2391            self.sc_stack.last(),
2392            prim_flags,
2393        );
2394
2395        if flags.contains(StackingContextFlags::IS_BLEND_CONTAINER) {
2401            if !self.sc_stack.is_empty() {
2403                blit_reason |= BlitReason::BLEND_MODE;
2406                is_redundant = false;
2407            } else {
2408                if self.tile_cache_builder.is_current_slice_empty() &&
2412                   self.spatial_tree.is_root_coord_system(spatial_node_index) &&
2413                   !self.clip_tree_builder.clip_node_has_complex_clips(clip_node_id, &self.interners)
2414                {
2415                    self.add_tile_cache_barrier_if_needed(SliceFlags::IS_ATOMIC);
2416                    self.tile_cache_builder.make_current_slice_atomic();
2417                } else {
2418                    blit_reason |= BlitReason::BLEND_MODE;
2422                    is_redundant = false;
2423                }
2424            }
2425        }
2426
2427        let set_tile_cache_barrier = prim_flags.contains(PrimitiveFlags::IS_SCROLLBAR_CONTAINER);
2430
2431        if set_tile_cache_barrier {
2432            self.add_tile_cache_barrier_if_needed(SliceFlags::IS_SCROLLBAR);
2433        }
2434
2435        let mut sc_info = StackingContextInfo {
2436            pop_stacking_context: false,
2437            pop_containing_block: false,
2438            set_tile_cache_barrier,
2439            needs_extra_stacking_context,
2440        };
2441
2442        if !participating_in_3d_context {
2444            sc_info.pop_containing_block = true;
2445            self.containing_block_stack.push(spatial_node_index);
2446        }
2447
2448        if !is_redundant {
2450            sc_info.pop_stacking_context = true;
2451
2452            self.sc_stack.push(FlattenedStackingContext {
2455                prim_list: PrimitiveList::empty(),
2456                prim_flags,
2457                spatial_node_index,
2458                clip_node_id,
2459                composite_ops,
2460                blit_reason,
2461                transform_style,
2462                context_3d,
2463                flags,
2464                raster_space: new_space,
2465                subregion_offset,
2466            });
2467        }
2468
2469        sc_info
2470    }
2471
2472    fn pop_stacking_context(
2473        &mut self,
2474        info: StackingContextInfo,
2475    ) {
2476        profile_scope!("pop_stacking_context");
2477
2478        self.clip_tree_builder.pop_clip();
2479
2480        self.raster_space_stack.pop().unwrap();
2482
2483        if info.pop_containing_block {
2485            self.containing_block_stack.pop().unwrap();
2486        }
2487
2488        if info.set_tile_cache_barrier {
2489            self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
2490        }
2491
2492        if !info.pop_stacking_context {
2494            return;
2495        }
2496
2497        let stacking_context = self.sc_stack.pop().unwrap();
2498
2499        let mut source = match stacking_context.context_3d {
2500            Picture3DContext::In { ancestor_index, plane_splitter_index, .. } => {
2508                let composite_mode = Some(
2509                    PictureCompositeMode::Blit(BlitReason::PRESERVE3D | stacking_context.blit_reason)
2510                );
2511
2512                let pic_index = PictureIndex(self.prim_store.pictures
2514                    .alloc()
2515                    .init(PicturePrimitive::new_image(
2516                        composite_mode.clone(),
2517                        Picture3DContext::In { root_data: None, ancestor_index, plane_splitter_index },
2518                        stacking_context.prim_flags,
2519                        stacking_context.prim_list,
2520                        stacking_context.spatial_node_index,
2521                        stacking_context.raster_space,
2522                        PictureFlags::empty(),
2523                        None,
2524                    ))
2525                );
2526
2527                let instance = create_prim_instance(
2528                    pic_index,
2529                    composite_mode.into(),
2530                    stacking_context.raster_space,
2531                    stacking_context.clip_node_id,
2532                    &mut self.interners,
2533                    &mut self.clip_tree_builder,
2534                );
2535
2536                PictureChainBuilder::from_instance(
2537                    instance,
2538                    stacking_context.prim_flags,
2539                    stacking_context.spatial_node_index,
2540                    stacking_context.raster_space,
2541                )
2542            }
2543            Picture3DContext::Out => {
2544                if stacking_context.blit_reason.is_empty() {
2545                    PictureChainBuilder::from_prim_list(
2546                        stacking_context.prim_list,
2547                        stacking_context.prim_flags,
2548                        stacking_context.spatial_node_index,
2549                        stacking_context.raster_space,
2550                        false,
2551                    )
2552                } else {
2553                    let composite_mode = Some(
2554                        PictureCompositeMode::Blit(stacking_context.blit_reason)
2555                    );
2556
2557                    let pic_index = PictureIndex(self.prim_store.pictures
2559                        .alloc()
2560                        .init(PicturePrimitive::new_image(
2561                            composite_mode.clone(),
2562                            Picture3DContext::Out,
2563                            stacking_context.prim_flags,
2564                            stacking_context.prim_list,
2565                            stacking_context.spatial_node_index,
2566                            stacking_context.raster_space,
2567                            PictureFlags::empty(),
2568                            None,
2569                        ))
2570                    );
2571
2572                    let instance = create_prim_instance(
2573                        pic_index,
2574                        composite_mode.into(),
2575                        stacking_context.raster_space,
2576                        stacking_context.clip_node_id,
2577                        &mut self.interners,
2578                        &mut self.clip_tree_builder,
2579                    );
2580
2581                    PictureChainBuilder::from_instance(
2582                        instance,
2583                        stacking_context.prim_flags,
2584                        stacking_context.spatial_node_index,
2585                        stacking_context.raster_space,
2586                    )
2587                }
2588            }
2589        };
2590
2591        if let Picture3DContext::In { root_data: Some(mut prims), ancestor_index, plane_splitter_index } = stacking_context.context_3d {
2595            let instance = source.finalize(
2596                ClipNodeId::NONE,
2597                &mut self.interners,
2598                &mut self.prim_store,
2599                &mut self.clip_tree_builder,
2600                None,
2601            );
2602
2603            prims.push(ExtendedPrimitiveInstance {
2604                instance,
2605                spatial_node_index: stacking_context.spatial_node_index,
2606                flags: stacking_context.prim_flags,
2607            });
2608
2609            let mut prim_list = PrimitiveList::empty();
2610
2611            let mut needs_3d_context = false;
2624
2625            for ext_prim in prims.drain(..) {
2626                if !self.spatial_tree.is_root_coord_system(ext_prim.spatial_node_index) {
2631                    needs_3d_context = true;
2632                }
2633
2634                prim_list.add_prim(
2635                    ext_prim.instance,
2636                    LayoutRect::zero(),
2637                    ext_prim.spatial_node_index,
2638                    ext_prim.flags,
2639                    &mut self.prim_instances,
2640                    &self.clip_tree_builder,
2641                );
2642            }
2643
2644            let context_3d = if needs_3d_context {
2645                Picture3DContext::In {
2646                    root_data: Some(Vec::new()),
2647                    ancestor_index,
2648                    plane_splitter_index,
2649                }
2650            } else {
2651                for child_pic_index in &prim_list.child_pictures {
2655                    let child_pic = &mut self.prim_store.pictures[child_pic_index.0];
2656                    let needs_surface = child_pic.snapshot.is_some();
2657                    if !needs_surface {
2658                        child_pic.composite_mode = None;
2659                    }
2660                    child_pic.context_3d = Picture3DContext::Out;
2661                }
2662
2663                Picture3DContext::Out
2664            };
2665
2666            let pic_index = PictureIndex(self.prim_store.pictures
2668                .alloc()
2669                .init(PicturePrimitive::new_image(
2670                    None,
2671                    context_3d,
2672                    stacking_context.prim_flags,
2673                    prim_list,
2674                    stacking_context.spatial_node_index,
2675                    stacking_context.raster_space,
2676                    PictureFlags::empty(),
2677                    None,
2678                ))
2679            );
2680
2681            let instance = create_prim_instance(
2682                pic_index,
2683                PictureCompositeKey::Identity,
2684                stacking_context.raster_space,
2685                stacking_context.clip_node_id,
2686                &mut self.interners,
2687                &mut self.clip_tree_builder,
2688            );
2689
2690            source = PictureChainBuilder::from_instance(
2691                instance,
2692                stacking_context.prim_flags,
2693                stacking_context.spatial_node_index,
2694                stacking_context.raster_space,
2695            );
2696        }
2697
2698        let has_filters = stacking_context.composite_ops.has_valid_filters();
2699
2700        let spatial_node_context_offset =
2701            stacking_context.subregion_offset +
2702            self.current_external_scroll_offset(stacking_context.spatial_node_index);
2703        source = self.wrap_prim_with_filters(
2704            source,
2705            stacking_context.clip_node_id,
2706            stacking_context.composite_ops.filters,
2707            stacking_context.composite_ops.filter_primitives,
2708            stacking_context.composite_ops.filter_datas,
2709            false,
2710            spatial_node_context_offset,
2711        );
2712
2713        if let Some(mix_blend_mode) = stacking_context.composite_ops.mix_blend_mode {
2726            let composite_mode = PictureCompositeMode::MixBlend(mix_blend_mode);
2727
2728            source = source.add_picture(
2729                composite_mode,
2730                stacking_context.clip_node_id,
2731                Picture3DContext::Out,
2732                &mut self.interners,
2733                &mut self.prim_store,
2734                &mut self.prim_instances,
2735                &mut self.clip_tree_builder,
2736            );
2737        }
2738
2739        let cur_instance = source.finalize(
2742            stacking_context.clip_node_id,
2743            &mut self.interners,
2744            &mut self.prim_store,
2745            &mut self.clip_tree_builder,
2746            stacking_context.composite_ops.snapshot,
2747        );
2748
2749        if stacking_context.composite_ops.snapshot.is_some() {
2750            let pic_index = cur_instance.kind.as_pic();
2751            self.snapshot_pictures.push(pic_index);
2752        }
2753
2754        let trailing_children_instance = match self.sc_stack.last_mut() {
2757            Some(ref parent_sc) if !has_filters && parent_sc.is_3d() => {
2759                Some(cur_instance)
2760            }
2761            Some(ref mut parent_sc) => {
2763                parent_sc.prim_list.add_prim(
2764                    cur_instance,
2765                    LayoutRect::zero(),
2766                    stacking_context.spatial_node_index,
2767                    stacking_context.prim_flags,
2768                    &mut self.prim_instances,
2769                    &self.clip_tree_builder,
2770                );
2771                None
2772            }
2773            None => {
2775                self.add_primitive_to_draw_list(
2776                    cur_instance,
2777                    LayoutRect::zero(),
2778                    stacking_context.spatial_node_index,
2779                    stacking_context.prim_flags,
2780                );
2781
2782                None
2783            }
2784        };
2785
2786        if let Some(instance) = trailing_children_instance {
2789            self.add_primitive_instance_to_3d_root(ExtendedPrimitiveInstance {
2790                instance,
2791                spatial_node_index: stacking_context.spatial_node_index,
2792                flags: stacking_context.prim_flags,
2793            });
2794        }
2795
2796        assert!(
2797            self.pending_shadow_items.is_empty(),
2798            "Found unpopped shadows when popping stacking context!"
2799        );
2800
2801        if info.needs_extra_stacking_context {
2802            let inner_info = self.extra_stacking_context_stack.pop().unwrap();
2803            self.pop_stacking_context(inner_info);
2804        }
2805    }
2806
2807    pub fn push_reference_frame(
2808        &mut self,
2809        reference_frame_id: SpatialId,
2810        parent_index: SpatialNodeIndex,
2811        pipeline_id: PipelineId,
2812        transform_style: TransformStyle,
2813        source_transform: PropertyBinding<LayoutTransform>,
2814        kind: ReferenceFrameKind,
2815        origin_in_parent_reference_frame: LayoutVector2D,
2816        uid: SpatialNodeUid,
2817    ) -> SpatialNodeIndex {
2818        let index = self.spatial_tree.add_reference_frame(
2819            parent_index,
2820            transform_style,
2821            source_transform,
2822            kind,
2823            origin_in_parent_reference_frame,
2824            pipeline_id,
2825            uid,
2826        );
2827        self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(reference_frame_id, index);
2828
2829        index
2830    }
2831
2832    fn push_root(
2833        &mut self,
2834        pipeline_id: PipelineId,
2835        instance: PipelineInstanceId,
2836    ) {
2837        let spatial_node_index = self.push_reference_frame(
2838            SpatialId::root_reference_frame(pipeline_id),
2839            self.spatial_tree.root_reference_frame_index(),
2840            pipeline_id,
2841            TransformStyle::Flat,
2842            PropertyBinding::Value(LayoutTransform::identity()),
2843            ReferenceFrameKind::Transform {
2844                is_2d_scale_translation: true,
2845                should_snap: true,
2846                paired_with_perspective: false,
2847            },
2848            LayoutVector2D::zero(),
2849            SpatialNodeUid::root_reference_frame(pipeline_id, instance),
2850        );
2851
2852        let viewport_rect = LayoutRect::max_rect();
2853
2854        self.add_scroll_frame(
2855            SpatialId::root_scroll_node(pipeline_id),
2856            spatial_node_index,
2857            ExternalScrollId(0, pipeline_id),
2858            pipeline_id,
2859            &viewport_rect,
2860            &viewport_rect.size(),
2861            ScrollFrameKind::PipelineRoot {
2862                is_root_pipeline: true,
2863            },
2864            LayoutVector2D::zero(),
2865            APZScrollGeneration::default(),
2866            HasScrollLinkedEffect::No,
2867            SpatialNodeUid::root_scroll_frame(pipeline_id, instance),
2868        );
2869    }
2870
2871    fn add_image_mask_clip_node(
2872        &mut self,
2873        new_node_id: ClipId,
2874        spatial_id: SpatialId,
2875        image_mask: &ImageMask,
2876        fill_rule: FillRule,
2877        points_range: ItemRange<LayoutPoint>,
2878    ) {
2879        let spatial_node_index = self.get_space(spatial_id);
2880
2881        let snapped_mask_rect = self.normalize_scroll_offset_and_snap_rect(
2882            &image_mask.rect,
2883            spatial_node_index,
2884        );
2885
2886        let points: Vec<LayoutPoint> = points_range.iter().collect();
2887
2888        let mut polygon_handle: Option<PolygonDataHandle> = None;
2890        if points.len() > 0 {
2891            let item = PolygonKey::new(&points, fill_rule);
2892
2893            let handle = self
2894                .interners
2895                .polygon
2896                .intern(&item, || item);
2897            polygon_handle = Some(handle);
2898        }
2899
2900        let item = ClipItemKey {
2901            kind: ClipItemKeyKind::image_mask(image_mask, snapped_mask_rect, polygon_handle),
2902            spatial_node_index,
2903        };
2904
2905        let handle = self
2906            .interners
2907            .clip
2908            .intern(&item, || {
2909                ClipInternData {
2910                    key: item,
2911                }
2912            });
2913
2914        self.clip_tree_builder.define_image_mask_clip(
2915            new_node_id,
2916            handle,
2917        );
2918    }
2919
2920    fn add_rect_clip_node(
2922        &mut self,
2923        new_node_id: ClipId,
2924        spatial_id: SpatialId,
2925        clip_rect: &LayoutRect,
2926    ) {
2927        let spatial_node_index = self.get_space(spatial_id);
2928
2929        let snapped_clip_rect = self.normalize_scroll_offset_and_snap_rect(
2930            clip_rect,
2931            spatial_node_index,
2932        );
2933
2934        let item = ClipItemKey {
2935            kind: ClipItemKeyKind::rectangle(snapped_clip_rect, ClipMode::Clip),
2936            spatial_node_index,
2937        };
2938        let handle = self
2939            .interners
2940            .clip
2941            .intern(&item, || {
2942                ClipInternData {
2943                    key: item,
2944                }
2945            });
2946
2947        self.clip_tree_builder.define_rect_clip(
2948            new_node_id,
2949            handle,
2950        );
2951    }
2952
2953    fn add_rounded_rect_clip_node(
2954        &mut self,
2955        new_node_id: ClipId,
2956        spatial_id: SpatialId,
2957        clip: &ComplexClipRegion,
2958    ) {
2959        let spatial_node_index = self.get_space(spatial_id);
2960
2961        let snapped_region_rect = self.normalize_scroll_offset_and_snap_rect(
2962            &clip.rect,
2963            spatial_node_index,
2964        );
2965
2966        let item = ClipItemKey {
2967            kind: ClipItemKeyKind::rounded_rect(
2968                snapped_region_rect,
2969                clip.radii,
2970                clip.mode,
2971            ),
2972            spatial_node_index,
2973        };
2974
2975        let handle = self
2976            .interners
2977            .clip
2978            .intern(&item, || {
2979                ClipInternData {
2980                    key: item,
2981                }
2982            });
2983
2984        self.clip_tree_builder.define_rounded_rect_clip(
2985            new_node_id,
2986            handle,
2987        );
2988    }
2989
2990    pub fn add_scroll_frame(
2991        &mut self,
2992        new_node_id: SpatialId,
2993        parent_node_index: SpatialNodeIndex,
2994        external_id: ExternalScrollId,
2995        pipeline_id: PipelineId,
2996        frame_rect: &LayoutRect,
2997        content_size: &LayoutSize,
2998        frame_kind: ScrollFrameKind,
2999        external_scroll_offset: LayoutVector2D,
3000        scroll_offset_generation: APZScrollGeneration,
3001        has_scroll_linked_effect: HasScrollLinkedEffect,
3002        uid: SpatialNodeUid,
3003    ) -> SpatialNodeIndex {
3004        let node_index = self.spatial_tree.add_scroll_frame(
3005            parent_node_index,
3006            external_id,
3007            pipeline_id,
3008            frame_rect,
3009            content_size,
3010            frame_kind,
3011            external_scroll_offset,
3012            scroll_offset_generation,
3013            has_scroll_linked_effect,
3014            uid,
3015        );
3016        self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(new_node_id, node_index);
3017        node_index
3018    }
3019
3020    pub fn push_shadow(
3021        &mut self,
3022        shadow: Shadow,
3023        spatial_node_index: SpatialNodeIndex,
3024        clip_chain_id: api::ClipChainId,
3025        should_inflate: bool,
3026    ) {
3027        self.clip_tree_builder.push_clip_chain(Some(clip_chain_id), false, false);
3028
3029        self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
3032            shadow,
3033            spatial_node_index,
3034            should_inflate,
3035        }));
3036    }
3037
3038    pub fn pop_all_shadows(
3039        &mut self,
3040    ) {
3041        assert!(!self.pending_shadow_items.is_empty(), "popped shadows, but none were present");
3042
3043        let mut items = mem::replace(&mut self.pending_shadow_items, VecDeque::new());
3044
3045        while let Some(item) = items.pop_front() {
3059            match item {
3060                ShadowItem::Shadow(pending_shadow) => {
3061                    let std_deviation = pending_shadow.shadow.blur_radius * 0.5;
3065
3066                    let mut prim_list = PrimitiveList::empty();
3069                    let blur_filter = Filter::Blur {
3070                        width: std_deviation,
3071                        height: std_deviation,
3072                        should_inflate: pending_shadow.should_inflate,
3073                        edge_mode: BlurEdgeMode::Duplicate,
3074                    };
3075                    let blur_is_noop = blur_filter.is_noop();
3076
3077                    for item in &items {
3078                        let (instance, info, spatial_node_index) = match item {
3079                            ShadowItem::Image(ref pending_image) => {
3080                                self.create_shadow_prim(
3081                                    &pending_shadow,
3082                                    pending_image,
3083                                    blur_is_noop,
3084                                )
3085                            }
3086                            ShadowItem::LineDecoration(ref pending_line_dec) => {
3087                                self.create_shadow_prim(
3088                                    &pending_shadow,
3089                                    pending_line_dec,
3090                                    blur_is_noop,
3091                                )
3092                            }
3093                            ShadowItem::NormalBorder(ref pending_border) => {
3094                                self.create_shadow_prim(
3095                                    &pending_shadow,
3096                                    pending_border,
3097                                    blur_is_noop,
3098                                )
3099                            }
3100                            ShadowItem::Primitive(ref pending_primitive) => {
3101                                self.create_shadow_prim(
3102                                    &pending_shadow,
3103                                    pending_primitive,
3104                                    blur_is_noop,
3105                                )
3106                            }
3107                            ShadowItem::TextRun(ref pending_text_run) => {
3108                                self.create_shadow_prim(
3109                                    &pending_shadow,
3110                                    pending_text_run,
3111                                    blur_is_noop,
3112                                )
3113                            }
3114                            _ => {
3115                                continue;
3116                            }
3117                        };
3118
3119                        if blur_is_noop {
3120                            self.add_primitive_to_draw_list(
3121                                instance,
3122                                info.rect,
3123                                spatial_node_index,
3124                                info.flags,
3125                            );
3126                        } else {
3127                            prim_list.add_prim(
3128                                instance,
3129                                info.rect,
3130                                spatial_node_index,
3131                                info.flags,
3132                                &mut self.prim_instances,
3133                                &self.clip_tree_builder,
3134                            );
3135                        }
3136                    }
3137
3138                    if !prim_list.is_empty() {
3141                        assert!(!blur_filter.is_noop());
3146                        let composite_mode = Some(PictureCompositeMode::Filter(blur_filter));
3147                        let composite_mode_key = composite_mode.clone().into();
3148                        let raster_space = RasterSpace::Screen;
3149
3150                        let shadow_pic_index = PictureIndex(self.prim_store.pictures
3152                            .alloc()
3153                            .init(PicturePrimitive::new_image(
3154                                composite_mode,
3155                                Picture3DContext::Out,
3156                                PrimitiveFlags::IS_BACKFACE_VISIBLE,
3157                                prim_list,
3158                                pending_shadow.spatial_node_index,
3159                                raster_space,
3160                                PictureFlags::empty(),
3161                                None,
3162                            ))
3163                        );
3164
3165                        let shadow_pic_key = PictureKey::new(
3166                            Picture { composite_mode_key, raster_space },
3167                        );
3168
3169                        let shadow_prim_data_handle = self.interners
3170                            .picture
3171                            .intern(&shadow_pic_key, || ());
3172
3173                        let clip_node_id = self.clip_tree_builder.build_clip_set(api::ClipChainId::INVALID);
3174
3175                        let shadow_prim_instance = PrimitiveInstance::new(
3176                            PrimitiveInstanceKind::Picture {
3177                                data_handle: shadow_prim_data_handle,
3178                                pic_index: shadow_pic_index,
3179                            },
3180                            self.clip_tree_builder.build_for_picture(clip_node_id),
3181                        );
3182
3183                        self.add_primitive_to_draw_list(
3186                            shadow_prim_instance,
3187                            LayoutRect::zero(),
3188                            pending_shadow.spatial_node_index,
3189                            PrimitiveFlags::IS_BACKFACE_VISIBLE,
3190                        );
3191                    }
3192
3193                    self.clip_tree_builder.pop_clip();
3194                }
3195                ShadowItem::Image(pending_image) => {
3196                    self.add_shadow_prim_to_draw_list(
3197                        pending_image,
3198                    )
3199                },
3200                ShadowItem::LineDecoration(pending_line_dec) => {
3201                    self.add_shadow_prim_to_draw_list(
3202                        pending_line_dec,
3203                    )
3204                },
3205                ShadowItem::NormalBorder(pending_border) => {
3206                    self.add_shadow_prim_to_draw_list(
3207                        pending_border,
3208                    )
3209                },
3210                ShadowItem::Primitive(pending_primitive) => {
3211                    self.add_shadow_prim_to_draw_list(
3212                        pending_primitive,
3213                    )
3214                },
3215                ShadowItem::TextRun(pending_text_run) => {
3216                    self.add_shadow_prim_to_draw_list(
3217                        pending_text_run,
3218                    )
3219                },
3220            }
3221        }
3222
3223        debug_assert!(items.is_empty());
3224        self.pending_shadow_items = items;
3225    }
3226
3227    fn create_shadow_prim<P>(
3228        &mut self,
3229        pending_shadow: &PendingShadow,
3230        pending_primitive: &PendingPrimitive<P>,
3231        blur_is_noop: bool,
3232    ) -> (PrimitiveInstance, LayoutPrimitiveInfo, SpatialNodeIndex)
3233    where
3234        P: InternablePrimitive + CreateShadow,
3235        Interners: AsMut<Interner<P>>,
3236    {
3237        let mut info = pending_primitive.info.clone();
3243        info.rect = info.rect.translate(pending_shadow.shadow.offset);
3244        info.clip_rect = info.clip_rect.translate(pending_shadow.shadow.offset);
3245
3246        let clip_set = self.clip_tree_builder.build_for_prim(
3247            pending_primitive.clip_node_id,
3248            &info,
3249            &[],
3250            &mut self.interners,
3251        );
3252
3253        let shadow_prim_instance = self.create_primitive(
3255            &info,
3256            clip_set,
3257            pending_primitive.prim.create_shadow(
3258                &pending_shadow.shadow,
3259                blur_is_noop,
3260                self.raster_space_stack.last().cloned().unwrap(),
3261            ),
3262        );
3263
3264        (shadow_prim_instance, info, pending_primitive.spatial_node_index)
3265    }
3266
3267    fn add_shadow_prim_to_draw_list<P>(
3268        &mut self,
3269        pending_primitive: PendingPrimitive<P>,
3270    ) where
3271        P: InternablePrimitive + IsVisible,
3272        Interners: AsMut<Interner<P>>,
3273    {
3274        if pending_primitive.prim.is_visible() {
3277            let clip_set = self.clip_tree_builder.build_for_prim(
3278                pending_primitive.clip_node_id,
3279                &pending_primitive.info,
3280                &[],
3281                &mut self.interners,
3282            );
3283
3284            self.add_prim_to_draw_list(
3285                &pending_primitive.info,
3286                pending_primitive.spatial_node_index,
3287                clip_set,
3288                pending_primitive.prim,
3289            );
3290        }
3291    }
3292
3293    pub fn add_clear_rectangle(
3294        &mut self,
3295        spatial_node_index: SpatialNodeIndex,
3296        clip_node_id: ClipNodeId,
3297        info: &LayoutPrimitiveInfo,
3298    ) {
3299        self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
3302
3303        self.add_primitive(
3304            spatial_node_index,
3305            clip_node_id,
3306            info,
3307            Vec::new(),
3308            PrimitiveKeyKind::Clear,
3309        );
3310
3311        self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
3312    }
3313
3314    pub fn add_line(
3315        &mut self,
3316        spatial_node_index: SpatialNodeIndex,
3317        clip_node_id: ClipNodeId,
3318        info: &LayoutPrimitiveInfo,
3319        wavy_line_thickness: f32,
3320        orientation: LineOrientation,
3321        color: ColorF,
3322        style: LineStyle,
3323    ) {
3324        let size = get_line_decoration_size(
3328            &info.rect.size(),
3329            orientation,
3330            style,
3331            wavy_line_thickness,
3332        );
3333
3334        let cache_key = size.map(|size| {
3335            LineDecorationCacheKey {
3336                style,
3337                orientation,
3338                wavy_line_thickness: Au::from_f32_px(wavy_line_thickness),
3339                size: size.to_au(),
3340            }
3341        });
3342
3343        self.add_primitive(
3344            spatial_node_index,
3345            clip_node_id,
3346            &info,
3347            Vec::new(),
3348            LineDecoration {
3349                cache_key,
3350                color: color.into(),
3351            },
3352        );
3353    }
3354
3355    pub fn add_border(
3356        &mut self,
3357        spatial_node_index: SpatialNodeIndex,
3358        clip_node_id: ClipNodeId,
3359        info: &LayoutPrimitiveInfo,
3360        border_item: &BorderDisplayItem,
3361        gradient_stops: ItemRange<GradientStop>,
3362    ) {
3363        match border_item.details {
3364            BorderDetails::NinePatch(ref border) => {
3365                let nine_patch = NinePatchDescriptor {
3366                    width: border.width,
3367                    height: border.height,
3368                    slice: border.slice,
3369                    fill: border.fill,
3370                    repeat_horizontal: border.repeat_horizontal,
3371                    repeat_vertical: border.repeat_vertical,
3372                    widths: border_item.widths.into(),
3373                };
3374
3375                match border.source {
3376                    NinePatchBorderSource::Image(key, rendering) => {
3377                        let prim = ImageBorder {
3378                            request: ImageRequest {
3379                                key,
3380                                rendering,
3381                                tile: None,
3382                            },
3383                            nine_patch,
3384                        };
3385
3386                        self.add_nonshadowable_primitive(
3387                            spatial_node_index,
3388                            clip_node_id,
3389                            info,
3390                            Vec::new(),
3391                            prim,
3392                        );
3393                    }
3394                    NinePatchBorderSource::Gradient(gradient) => {
3395                        let prim = match self.create_linear_gradient_prim(
3396                            &info,
3397                            gradient.start_point,
3398                            gradient.end_point,
3399                            read_gradient_stops(gradient_stops),
3400                            gradient.extend_mode,
3401                            LayoutSize::new(border.height as f32, border.width as f32),
3402                            LayoutSize::zero(),
3403                            Some(Box::new(nine_patch)),
3404                            EdgeAaSegmentMask::all(),
3405                        ) {
3406                            Some(prim) => prim,
3407                            None => return,
3408                        };
3409
3410                        self.add_nonshadowable_primitive(
3411                            spatial_node_index,
3412                            clip_node_id,
3413                            info,
3414                            Vec::new(),
3415                            prim,
3416                        );
3417                    }
3418                    NinePatchBorderSource::RadialGradient(gradient) => {
3419                        let prim = self.create_radial_gradient_prim(
3420                            &info,
3421                            gradient.center,
3422                            gradient.start_offset * gradient.radius.width,
3423                            gradient.end_offset * gradient.radius.width,
3424                            gradient.radius.width / gradient.radius.height,
3425                            read_gradient_stops(gradient_stops),
3426                            gradient.extend_mode,
3427                            LayoutSize::new(border.height as f32, border.width as f32),
3428                            LayoutSize::zero(),
3429                            Some(Box::new(nine_patch)),
3430                        );
3431
3432                        self.add_nonshadowable_primitive(
3433                            spatial_node_index,
3434                            clip_node_id,
3435                            info,
3436                            Vec::new(),
3437                            prim,
3438                        );
3439                    }
3440                    NinePatchBorderSource::ConicGradient(gradient) => {
3441                        let prim = self.create_conic_gradient_prim(
3442                            &info,
3443                            gradient.center,
3444                            gradient.angle,
3445                            gradient.start_offset,
3446                            gradient.end_offset,
3447                            gradient_stops,
3448                            gradient.extend_mode,
3449                            LayoutSize::new(border.height as f32, border.width as f32),
3450                            LayoutSize::zero(),
3451                            Some(Box::new(nine_patch)),
3452                        );
3453
3454                        self.add_nonshadowable_primitive(
3455                            spatial_node_index,
3456                            clip_node_id,
3457                            info,
3458                            Vec::new(),
3459                            prim,
3460                        );
3461                    }
3462                };
3463            }
3464            BorderDetails::Normal(ref border) => {
3465                self.add_normal_border(
3466                    info,
3467                    border,
3468                    border_item.widths,
3469                    spatial_node_index,
3470                    clip_node_id,
3471                );
3472            }
3473        }
3474    }
3475
3476    pub fn create_linear_gradient_prim(
3477        &mut self,
3478        info: &LayoutPrimitiveInfo,
3479        start_point: LayoutPoint,
3480        end_point: LayoutPoint,
3481        stops: Vec<GradientStopKey>,
3482        extend_mode: ExtendMode,
3483        stretch_size: LayoutSize,
3484        mut tile_spacing: LayoutSize,
3485        nine_patch: Option<Box<NinePatchDescriptor>>,
3486        edge_aa_mask: EdgeAaSegmentMask,
3487    ) -> Option<LinearGradient> {
3488        let mut prim_rect = info.rect;
3489        simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3490
3491        let mut has_hard_stops = false;
3492        let mut is_entirely_transparent = true;
3493        let mut prev_stop = None;
3494        for stop in &stops {
3495            if Some(stop.offset) == prev_stop {
3496                has_hard_stops = true;
3497            }
3498            prev_stop = Some(stop.offset);
3499            if stop.color.a > 0 {
3500                is_entirely_transparent = false;
3501            }
3502        }
3503
3504        if is_entirely_transparent {
3507            return None;
3508        }
3509
3510        let reverse_stops = start_point.x > end_point.x ||
3517            (start_point.x == end_point.x && start_point.y > end_point.y);
3518
3519        let (sp, ep) = if reverse_stops {
3523            (end_point, start_point)
3524        } else {
3525            (start_point, end_point)
3526        };
3527
3528        let max = gradient::LINEAR_MAX_CACHED_SIZE;
3532        let caching_causes_artifacts = has_hard_stops && (stretch_size.width > max || stretch_size.height > max);
3533
3534        let is_tiled = prim_rect.width() > stretch_size.width
3535         || prim_rect.height() > stretch_size.height;
3536        let cached = (!self.config.is_software || is_tiled) && !caching_causes_artifacts;
3540
3541        Some(LinearGradient {
3542            extend_mode,
3543            start_point: sp.into(),
3544            end_point: ep.into(),
3545            stretch_size: stretch_size.into(),
3546            tile_spacing: tile_spacing.into(),
3547            stops,
3548            reverse_stops,
3549            nine_patch,
3550            cached,
3551            edge_aa_mask,
3552            enable_dithering: self.config.enable_dithering,
3553        })
3554    }
3555
3556    pub fn create_radial_gradient_prim(
3557        &mut self,
3558        info: &LayoutPrimitiveInfo,
3559        center: LayoutPoint,
3560        start_radius: f32,
3561        end_radius: f32,
3562        ratio_xy: f32,
3563        stops: Vec<GradientStopKey>,
3564        extend_mode: ExtendMode,
3565        stretch_size: LayoutSize,
3566        mut tile_spacing: LayoutSize,
3567        nine_patch: Option<Box<NinePatchDescriptor>>,
3568    ) -> RadialGradient {
3569        let mut prim_rect = info.rect;
3570        simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3571
3572        let params = RadialGradientParams {
3573            start_radius,
3574            end_radius,
3575            ratio_xy,
3576        };
3577
3578        RadialGradient {
3579            extend_mode,
3580            center: center.into(),
3581            params,
3582            stretch_size: stretch_size.into(),
3583            tile_spacing: tile_spacing.into(),
3584            nine_patch,
3585            stops,
3586        }
3587    }
3588
3589    pub fn create_conic_gradient_prim(
3590        &mut self,
3591        info: &LayoutPrimitiveInfo,
3592        center: LayoutPoint,
3593        angle: f32,
3594        start_offset: f32,
3595        end_offset: f32,
3596        stops: ItemRange<GradientStop>,
3597        extend_mode: ExtendMode,
3598        stretch_size: LayoutSize,
3599        mut tile_spacing: LayoutSize,
3600        nine_patch: Option<Box<NinePatchDescriptor>>,
3601    ) -> ConicGradient {
3602        let mut prim_rect = info.rect;
3603        simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3604
3605        let stops = stops.iter().map(|stop| {
3606            GradientStopKey {
3607                offset: stop.offset,
3608                color: stop.color.into(),
3609            }
3610        }).collect();
3611
3612        ConicGradient {
3613            extend_mode,
3614            center: center.into(),
3615            params: ConicGradientParams { angle, start_offset, end_offset },
3616            stretch_size: stretch_size.into(),
3617            tile_spacing: tile_spacing.into(),
3618            nine_patch,
3619            stops,
3620        }
3621    }
3622
3623    pub fn add_text(
3624        &mut self,
3625        spatial_node_index: SpatialNodeIndex,
3626        clip_node_id: ClipNodeId,
3627        prim_info: &LayoutPrimitiveInfo,
3628        font_instance_key: &FontInstanceKey,
3629        text_color: &ColorF,
3630        glyph_range: ItemRange<GlyphInstance>,
3631        glyph_options: Option<GlyphOptions>,
3632        ref_frame_offset: LayoutVector2D,
3633    ) {
3634        let offset = self.current_external_scroll_offset(spatial_node_index) + ref_frame_offset;
3635
3636        let text_run = {
3637            let shared_key = self.fonts.instance_keys.map_key(font_instance_key);
3638            let font_instance = match self.fonts.instances.get_font_instance(shared_key) {
3639                Some(instance) => instance,
3640                None => {
3641                    warn!("Unknown font instance key");
3642                    debug!("key={:?} shared={:?}", font_instance_key, shared_key);
3643                    return;
3644                }
3645            };
3646
3647            if font_instance.size <= FontSize::zero() {
3649                return;
3650            }
3651
3652            let mut render_mode = self.config
3656                .default_font_render_mode
3657                .limit_by(font_instance.render_mode);
3658            let mut flags = font_instance.flags;
3659            if let Some(options) = glyph_options {
3660                render_mode = render_mode.limit_by(options.render_mode);
3661                flags |= options.flags;
3662            }
3663
3664            let font = FontInstance::new(
3665                font_instance,
3666                (*text_color).into(),
3667                render_mode,
3668                flags,
3669            );
3670
3671            let prim_offset = prim_info.rect.min.to_vector() - offset;
3676            let glyphs = glyph_range
3677                .iter()
3678                .map(|glyph| {
3679                    GlyphInstance {
3680                        index: glyph.index,
3681                        point: glyph.point - prim_offset,
3682                    }
3683                })
3684                .collect();
3685
3686            let requested_raster_space = self.raster_space_stack
3689                .last()
3690                .cloned()
3691                .unwrap();
3692
3693            TextRun {
3694                glyphs,
3695                font,
3696                shadow: false,
3697                requested_raster_space,
3698                reference_frame_offset: ref_frame_offset,
3699            }
3700        };
3701
3702        self.add_primitive(
3703            spatial_node_index,
3704            clip_node_id,
3705            prim_info,
3706            Vec::new(),
3707            text_run,
3708        );
3709    }
3710
3711    pub fn add_image(
3712        &mut self,
3713        spatial_node_index: SpatialNodeIndex,
3714        clip_node_id: ClipNodeId,
3715        info: &LayoutPrimitiveInfo,
3716        stretch_size: LayoutSize,
3717        mut tile_spacing: LayoutSize,
3718        image_key: ImageKey,
3719        image_rendering: ImageRendering,
3720        alpha_type: AlphaType,
3721        color: ColorF,
3722    ) {
3723        let mut prim_rect = info.rect;
3724        simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3725        let info = LayoutPrimitiveInfo {
3726            rect: prim_rect,
3727            .. *info
3728        };
3729
3730        self.add_primitive(
3731            spatial_node_index,
3732            clip_node_id,
3733            &info,
3734            Vec::new(),
3735            Image {
3736                key: image_key,
3737                tile_spacing: tile_spacing.into(),
3738                stretch_size: stretch_size.into(),
3739                color: color.into(),
3740                image_rendering,
3741                alpha_type,
3742            },
3743        );
3744    }
3745
3746    pub fn add_yuv_image(
3747        &mut self,
3748        spatial_node_index: SpatialNodeIndex,
3749        clip_node_id: ClipNodeId,
3750        info: &LayoutPrimitiveInfo,
3751        yuv_data: YuvData,
3752        color_depth: ColorDepth,
3753        color_space: YuvColorSpace,
3754        color_range: ColorRange,
3755        image_rendering: ImageRendering,
3756    ) {
3757        let format = yuv_data.get_format();
3758        let yuv_key = match yuv_data {
3759            YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3760            YuvData::P010(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3761            YuvData::NV16(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3762            YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
3763            YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
3764        };
3765
3766        self.add_nonshadowable_primitive(
3767            spatial_node_index,
3768            clip_node_id,
3769            info,
3770            Vec::new(),
3771            YuvImage {
3772                color_depth,
3773                yuv_key,
3774                format,
3775                color_space,
3776                color_range,
3777                image_rendering,
3778            },
3779        );
3780    }
3781
3782    fn add_primitive_instance_to_3d_root(
3783        &mut self,
3784        prim: ExtendedPrimitiveInstance,
3785    ) {
3786        for sc in self.sc_stack.iter_mut().rev() {
3788            match sc.context_3d {
3789                Picture3DContext::In { root_data: Some(ref mut prims), .. } => {
3790                    prims.push(prim);
3791                    break;
3792                }
3793                Picture3DContext::In { .. } => {}
3794                Picture3DContext::Out => panic!("Unable to find 3D root"),
3795            }
3796        }
3797    }
3798
3799    #[allow(dead_code)]
3800    pub fn add_backdrop_filter(
3801        &mut self,
3802        spatial_node_index: SpatialNodeIndex,
3803        clip_node_id: ClipNodeId,
3804        info: &LayoutPrimitiveInfo,
3805        filters: Vec<Filter>,
3806        filter_datas: Vec<FilterData>,
3807        filter_primitives: Vec<FilterPrimitive>,
3808    ) {
3809        let filter_spatial_node_index = SpatialNodeIndex::UNKNOWN;
3813
3814        self.make_current_slice_atomic_if_required();
3815
3816        let clip_leaf_id = self.clip_tree_builder.build_for_prim(
3820            clip_node_id,
3821            info,
3822            &[],
3823            &mut self.interners,
3824        );
3825
3826        let backdrop_capture_instance = self.create_primitive(
3829            info,
3830            clip_leaf_id,
3831            BackdropCapture {
3832            },
3833        );
3834
3835        let mut prim_list = PrimitiveList::empty();
3838        prim_list.add_prim(
3839            backdrop_capture_instance,
3840            info.rect,
3841            spatial_node_index,
3842            info.flags,
3843            &mut self.prim_instances,
3844            &self.clip_tree_builder,
3845        );
3846
3847        let mut source = PictureChainBuilder::from_prim_list(
3848            prim_list,
3849            info.flags,
3850            filter_spatial_node_index,
3851            RasterSpace::Screen,
3852            true,
3853        );
3854
3855        source = self.wrap_prim_with_filters(
3858            source,
3859            clip_node_id,
3860            filters,
3861            filter_primitives,
3862            filter_datas,
3863            true,
3864            LayoutVector2D::zero(),
3865        );
3866
3867        if source.has_picture() {
3870            source = source.add_picture(
3871                PictureCompositeMode::IntermediateSurface,
3872                clip_node_id,
3873                Picture3DContext::Out,
3874                &mut self.interners,
3875                &mut self.prim_store,
3876                &mut self.prim_instances,
3877                &mut self.clip_tree_builder,
3878            );
3879
3880            let filtered_instance = source.finalize(
3881                clip_node_id,
3882                &mut self.interners,
3883                &mut self.prim_store,
3884                &mut self.clip_tree_builder,
3885                None,
3886            );
3887
3888            let output_pic_index = match filtered_instance.kind {
3891                PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index,
3892                _ => panic!("bug: not a picture"),
3893            };
3894
3895            let sc_index = self.sc_stack.iter().rposition(|sc| {
3898                !sc.flags.contains(StackingContextFlags::WRAPS_BACKDROP_FILTER)
3899            });
3900
3901            match sc_index {
3902                Some(sc_index) => {
3903                    self.sc_stack[sc_index].prim_list.add_prim(
3904                        filtered_instance,
3905                        info.rect,
3906                        filter_spatial_node_index,
3907                        info.flags,
3908                        &mut self.prim_instances,
3909                        &self.clip_tree_builder,
3910                    );
3911                }
3912                None => {
3913                    self.tile_cache_builder.add_prim(
3914                        filtered_instance,
3915                        info.rect,
3916                        filter_spatial_node_index,
3917                        info.flags,
3918                        self.spatial_tree,
3919                        self.interners,
3920                        &self.quality_settings,
3921                        &mut self.prim_instances,
3922                        &self.clip_tree_builder,
3923                    );
3924                }
3925            }
3926
3927            let mut backdrop_render_instance = self.create_primitive(
3929                info,
3930                clip_leaf_id,
3931                BackdropRender {
3932                },
3933            );
3934
3935            match backdrop_render_instance.kind {
3938                PrimitiveInstanceKind::BackdropRender { ref mut pic_index, .. } => {
3939                    assert_eq!(*pic_index, PictureIndex::INVALID);
3940                    *pic_index = output_pic_index;
3941                }
3942                _ => panic!("bug: unexpected prim kind"),
3943            }
3944
3945            self.add_primitive_to_draw_list(
3946                backdrop_render_instance,
3947                info.rect,
3948                spatial_node_index,
3949                info.flags,
3950            );
3951        }
3952    }
3953
3954    #[must_use]
3955    fn wrap_prim_with_filters(
3956        &mut self,
3957        mut source: PictureChainBuilder,
3958        clip_node_id: ClipNodeId,
3959        mut filter_ops: Vec<Filter>,
3960        mut filter_primitives: Vec<FilterPrimitive>,
3961        filter_datas: Vec<FilterData>,
3962        is_backdrop_filter: bool,
3963        context_offset: LayoutVector2D,
3964    ) -> PictureChainBuilder {
3965        assert!(filter_ops.is_empty() || filter_primitives.is_empty(),
3969            "Filter ops and filter primitives are not allowed on the same stacking context.");
3970
3971        let mut current_filter_data_index = 0;
3973        if let Some(Filter::SVGGraphNode(..)) = filter_ops.first() {
3978            const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX;
3987            const SVGFE_INFLATE: i16 = 1;
3990
3991            let mut reference_for_buffer_id: [FilterGraphPictureReference; BUFFER_LIMIT] = [
4011                FilterGraphPictureReference{
4012                    buffer_id: FilterOpGraphPictureBufferId::BufferId(-1),
4016                    subregion: LayoutRect::zero(), offset: LayoutVector2D::zero(),
4018                    inflate: 0,
4019                    source_padding: LayoutRect::zero(),
4020                    target_padding: LayoutRect::zero(),
4021                }; BUFFER_LIMIT];
4022            let mut filters: Vec<(FilterGraphNode, FilterGraphOp)> = Vec::new();
4023            filters.reserve(BUFFER_LIMIT);
4024            for (original_id, parsefilter) in filter_ops.iter().enumerate() {
4025                if filters.len() >= BUFFER_LIMIT {
4026                    return source;
4029                }
4030
4031                let newfilter = match parsefilter {
4032                    Filter::SVGGraphNode(parsenode, op) => {
4033                        let clip_region = parsenode.subregion
4037                            .translate(context_offset);
4038
4039                        let mut newnode = FilterGraphNode {
4040                            kept_by_optimizer: false,
4041                            linear: parsenode.linear,
4042                            inflate: SVGFE_INFLATE,
4043                            inputs: Vec::new(),
4044                            subregion: clip_region,
4045                        };
4046
4047                        let mut remapped_inputs: Vec<FilterGraphPictureReference> = Vec::new();
4050                        remapped_inputs.reserve_exact(parsenode.inputs.len());
4051                        for input in &parsenode.inputs {
4052                            match input.buffer_id {
4053                                FilterOpGraphPictureBufferId::BufferId(buffer_id) => {
4054                                    let pic = *reference_for_buffer_id
4057                                        .get(buffer_id as usize)
4058                                        .expect("BufferId not valid?");
4059                                    let offset = input.offset;
4064                                    let subregion = pic.subregion
4065                                        .translate(offset);
4066                                    let source_padding = LayoutRect::zero()
4067                                        .translate(-offset);
4068                                    let target_padding = LayoutRect::zero()
4069                                        .translate(offset);
4070                                    remapped_inputs.push(
4071                                        FilterGraphPictureReference {
4072                                            buffer_id: pic.buffer_id,
4073                                            subregion,
4074                                            offset,
4075                                            inflate: pic.inflate,
4076                                            source_padding,
4077                                            target_padding,
4078                                        });
4079                                }
4080                                FilterOpGraphPictureBufferId::None => panic!("Unsupported FilterOpGraphPictureBufferId"),
4081                            }
4082                        }
4083
4084                        fn union_unchecked(a: LayoutRect, b: LayoutRect) -> LayoutRect {
4085                            let mut r = a;
4086                            if r.min.x > b.min.x {r.min.x = b.min.x}
4087                            if r.min.y > b.min.y {r.min.y = b.min.y}
4088                            if r.max.x < b.max.x {r.max.x = b.max.x}
4089                            if r.max.y < b.max.y {r.max.y = b.max.y}
4090                            r
4091                        }
4092
4093                        match op {
4094                            FilterGraphOp::SVGFEFlood{..} |
4095                            FilterGraphOp::SVGFESourceAlpha |
4096                            FilterGraphOp::SVGFESourceGraphic |
4097                            FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} |
4098                            FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} |
4099                            FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} |
4100                            FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {
4101                                assert!(remapped_inputs.len() == 0);
4102                                (newnode.clone(), op.clone())
4103                            }
4104                            FilterGraphOp::SVGFEColorMatrix{..} |
4105                            FilterGraphOp::SVGFEIdentity |
4106                            FilterGraphOp::SVGFEImage{..} |
4107                            FilterGraphOp::SVGFEOpacity{..} |
4108                            FilterGraphOp::SVGFEToAlpha => {
4109                                assert!(remapped_inputs.len() == 1);
4110                                newnode.inputs = remapped_inputs;
4111                                (newnode.clone(), op.clone())
4112                            }
4113                            FilterGraphOp::SVGFEComponentTransfer => {
4114                                assert!(remapped_inputs.len() == 1);
4115                                let filter_data =
4117                                    &filter_datas[current_filter_data_index];
4118                                let filter_data = filter_data.sanitize();
4119                                current_filter_data_index = current_filter_data_index + 1;
4120
4121                                let creates_pixels =
4136                                    if let Some(a) = filter_data.r_values.get(3) {
4137                                        *a >= (0.5/255.0)
4138                                    } else {
4139                                        false
4140                                    };
4141                                let filter_data_key = SFilterDataKey {
4142                                    data:
4143                                        SFilterData {
4144                                            r_func: SFilterDataComponent::from_functype_values(
4145                                                filter_data.func_r_type, &filter_data.r_values),
4146                                            g_func: SFilterDataComponent::from_functype_values(
4147                                                filter_data.func_g_type, &filter_data.g_values),
4148                                            b_func: SFilterDataComponent::from_functype_values(
4149                                                filter_data.func_b_type, &filter_data.b_values),
4150                                            a_func: SFilterDataComponent::from_functype_values(
4151                                                filter_data.func_a_type, &filter_data.a_values),
4152                                        },
4153                                };
4154
4155                                let handle = self.interners
4156                                    .filter_data
4157                                    .intern(&filter_data_key, || ());
4158
4159                                newnode.inputs = remapped_inputs;
4160                                (newnode.clone(), FilterGraphOp::SVGFEComponentTransferInterned{handle, creates_pixels})
4161                            }
4162                            FilterGraphOp::SVGFEComponentTransferInterned{..} => unreachable!(),
4163                            FilterGraphOp::SVGFETile => {
4164                                assert!(remapped_inputs.len() == 1);
4165                                remapped_inputs[0].source_padding =
4167                                    LayoutRect::max_rect();
4168                                remapped_inputs[0].target_padding =
4169                                    LayoutRect::max_rect();
4170                                newnode.inputs = remapped_inputs;
4171                                (newnode.clone(), op.clone())
4172                            }
4173                            FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{kernel_unit_length_x, kernel_unit_length_y, ..} |
4174                            FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{kernel_unit_length_x, kernel_unit_length_y, ..} |
4175                            FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{kernel_unit_length_x, kernel_unit_length_y, ..} |
4176                            FilterGraphOp::SVGFEMorphologyDilate{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
4177                                assert!(remapped_inputs.len() == 1);
4178                                let padding = LayoutSize::new(
4179                                    kernel_unit_length_x.ceil(),
4180                                    kernel_unit_length_y.ceil(),
4181                                );
4182                                remapped_inputs[0].source_padding =
4185                                    remapped_inputs[0].source_padding
4186                                    .inflate(padding.width, padding.height);
4187                                remapped_inputs[0].target_padding =
4190                                    remapped_inputs[0].target_padding
4191                                    .inflate(padding.width, padding.height);
4192                                newnode.inputs = remapped_inputs;
4193                                (newnode.clone(), op.clone())
4194                            },
4195                            FilterGraphOp::SVGFEDiffuseLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
4196                            FilterGraphOp::SVGFEDiffuseLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
4197                            FilterGraphOp::SVGFEDiffuseLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
4198                            FilterGraphOp::SVGFESpecularLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
4199                            FilterGraphOp::SVGFESpecularLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
4200                            FilterGraphOp::SVGFESpecularLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
4201                            FilterGraphOp::SVGFEMorphologyErode{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
4202                                assert!(remapped_inputs.len() == 1);
4203                                let padding = LayoutSize::new(
4204                                    kernel_unit_length_x.ceil(),
4205                                    kernel_unit_length_y.ceil(),
4206                                );
4207                                remapped_inputs[0].source_padding =
4210                                    remapped_inputs[0].source_padding
4211                                    .inflate(padding.width, padding.height);
4212                                remapped_inputs[0].target_padding =
4215                                    remapped_inputs[0].target_padding
4216                                    .inflate(padding.width, padding.height);
4217                                newnode.inputs = remapped_inputs;
4218                                (newnode.clone(), op.clone())
4219                            },
4220                            FilterGraphOp::SVGFEDisplacementMap { scale, .. } => {
4221                                assert!(remapped_inputs.len() == 2);
4222                                let padding = LayoutSize::new(
4223                                    scale.ceil(),
4224                                    scale.ceil(),
4225                                );
4226                                remapped_inputs[0].source_padding =
4231                                    remapped_inputs[0].source_padding
4232                                    .inflate(padding.width, padding.height);
4233                                remapped_inputs[1].source_padding =
4234                                    remapped_inputs[1].source_padding
4235                                    .inflate(padding.width, padding.height);
4236                                remapped_inputs[0].target_padding =
4237                                    remapped_inputs[0].target_padding
4238                                    .inflate(padding.width, padding.height);
4239                                remapped_inputs[1].target_padding =
4240                                    remapped_inputs[1].target_padding
4241                                    .inflate(padding.width, padding.height);
4242                                newnode.inputs = remapped_inputs;
4243                                (newnode.clone(), op.clone())
4244                            },
4245                            FilterGraphOp::SVGFEDropShadow{ dx, dy, std_deviation_x, std_deviation_y, .. } => {
4246                                assert!(remapped_inputs.len() == 1);
4247                                let padding = LayoutSize::new(
4248                                    std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
4249                                    std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
4250                                );
4251                                remapped_inputs[0].source_padding =
4253                                    union_unchecked(
4254                                        remapped_inputs[0].source_padding,
4255                                        remapped_inputs[0].source_padding
4256                                            .inflate(padding.width, padding.height)
4257                                            .translate(
4258                                                LayoutVector2D::new(-dx, -dy)
4259                                            )
4260                                    );
4261                                remapped_inputs[0].target_padding =
4264                                    union_unchecked(
4265                                        remapped_inputs[0].target_padding,
4266                                        remapped_inputs[0].target_padding
4267                                            .inflate(padding.width, padding.height)
4268                                            .translate(
4269                                                LayoutVector2D::new(*dx, *dy)
4270                                            )
4271                                    );
4272                                newnode.inputs = remapped_inputs;
4273                                (newnode.clone(), op.clone())
4274                            },
4275                            FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} => {
4276                                assert!(remapped_inputs.len() == 1);
4277                                let padding = LayoutSize::new(
4278                                    std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
4279                                    std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
4280                                );
4281                                remapped_inputs[0].source_padding =
4283                                    remapped_inputs[0].source_padding
4284                                    .inflate(padding.width, padding.height);
4285                                remapped_inputs[0].target_padding =
4287                                    remapped_inputs[0].target_padding
4288                                    .inflate(padding.width, padding.height);
4289                                newnode.inputs = remapped_inputs;
4290                                (newnode.clone(), op.clone())
4291                            }
4292                            FilterGraphOp::SVGFEBlendColor |
4293                            FilterGraphOp::SVGFEBlendColorBurn |
4294                            FilterGraphOp::SVGFEBlendColorDodge |
4295                            FilterGraphOp::SVGFEBlendDarken |
4296                            FilterGraphOp::SVGFEBlendDifference |
4297                            FilterGraphOp::SVGFEBlendExclusion |
4298                            FilterGraphOp::SVGFEBlendHardLight |
4299                            FilterGraphOp::SVGFEBlendHue |
4300                            FilterGraphOp::SVGFEBlendLighten |
4301                            FilterGraphOp::SVGFEBlendLuminosity|
4302                            FilterGraphOp::SVGFEBlendMultiply |
4303                            FilterGraphOp::SVGFEBlendNormal |
4304                            FilterGraphOp::SVGFEBlendOverlay |
4305                            FilterGraphOp::SVGFEBlendSaturation |
4306                            FilterGraphOp::SVGFEBlendScreen |
4307                            FilterGraphOp::SVGFEBlendSoftLight |
4308                            FilterGraphOp::SVGFECompositeArithmetic{..} |
4309                            FilterGraphOp::SVGFECompositeATop |
4310                            FilterGraphOp::SVGFECompositeIn |
4311                            FilterGraphOp::SVGFECompositeLighter |
4312                            FilterGraphOp::SVGFECompositeOut |
4313                            FilterGraphOp::SVGFECompositeOver |
4314                            FilterGraphOp::SVGFECompositeXOR => {
4315                                assert!(remapped_inputs.len() == 2);
4316                                newnode.inputs = remapped_inputs;
4317                                (newnode, op.clone())
4318                            }
4319                        }
4320                    }
4321                    Filter::Opacity(valuebinding, value) => {
4322                        let pic = reference_for_buffer_id[original_id as usize - 1];
4328                        (
4329                            FilterGraphNode {
4330                                kept_by_optimizer: false,
4331                                linear: false,
4332                                inflate: SVGFE_INFLATE,
4333                                inputs: [pic].to_vec(),
4334                                subregion: pic.subregion,
4335                            },
4336                            FilterGraphOp::SVGFEOpacity{
4337                                valuebinding: *valuebinding,
4338                                value: *value,
4339                            },
4340                        )
4341                    }
4342                    _ => {
4343                        log!(Level::Warn, "wrap_prim_with_filters: unexpected filter after SVG filters filter[{:?}]={:?}", original_id, parsefilter);
4344                        return source;
4348                    }
4349                };
4350                let id = filters.len();
4351                filters.push(newfilter);
4352
4353                reference_for_buffer_id[original_id] = FilterGraphPictureReference {
4356                    buffer_id: FilterOpGraphPictureBufferId::BufferId(id as i16),
4357                    subregion: filters[id].0.subregion,
4358                    offset: LayoutVector2D::zero(),
4359                    inflate: filters[id].0.inflate,
4360                    source_padding: LayoutRect::zero(),
4361                    target_padding: LayoutRect::zero(),
4362                };
4363            }
4364
4365            if filters.len() >= BUFFER_LIMIT {
4366                return source;
4369            }
4370
4371            let mut kept_node_by_buffer_id = [false; BUFFER_LIMIT];
4379            kept_node_by_buffer_id[filters.len() - 1] = true;
4380            for (index, (node, _op)) in filters.iter_mut().enumerate().rev() {
4381                let mut keep = false;
4382                if let Some(k) = kept_node_by_buffer_id.get(index) {
4384                    if *k {
4385                        keep = true;
4386                    }
4387                }
4388                if keep {
4389                    node.kept_by_optimizer = true;
4393                    for input in &node.inputs {
4394                        if let FilterOpGraphPictureBufferId::BufferId(id) = input.buffer_id {
4395                            if let Some(k) = kept_node_by_buffer_id.get_mut(id as usize) {
4396                                *k = true;
4397                            }
4398                        }
4399                    }
4400                }
4401            }
4402
4403            let mut invalid_dag = false;
4406            for (id, (node, _op)) in filters.iter().enumerate() {
4407                for input in &node.inputs {
4408                    if let FilterOpGraphPictureBufferId::BufferId(buffer_id) = input.buffer_id {
4409                        if buffer_id < 0 || buffer_id as usize >= id {
4410                            invalid_dag = true;
4411                        }
4412                    }
4413                }
4414            }
4415
4416            if invalid_dag {
4417                log!(Level::Warn, "List of FilterOp::SVGGraphNode filter primitives appears to be invalid!");
4418                for (id, (node, op)) in filters.iter().enumerate() {
4419                    log!(Level::Warn, " node:     buffer=BufferId({}) op={} inflate={} subregion {:?} linear={} kept={}",
4420                         id, op.kind(), node.inflate,
4421                         node.subregion,
4422                         node.linear,
4423                         node.kept_by_optimizer,
4424                    );
4425                    for input in &node.inputs {
4426                        log!(Level::Warn, "input: buffer={} inflate={} subregion {:?} offset {:?} target_padding={:?} source_padding={:?}",
4427                            match input.buffer_id {
4428                                FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId({})", id),
4429                                FilterOpGraphPictureBufferId::None => "None".into(),
4430                            },
4431                            input.inflate,
4432                            input.subregion,
4433                            input.offset,
4434                            input.target_padding,
4435                            input.source_padding,
4436                        );
4437                    }
4438                }
4439            }
4440            if invalid_dag {
4441                return source;
4443            }
4444
4445            let composite_mode = PictureCompositeMode::SVGFEGraph(
4446                filters,
4447            );
4448
4449            source = source.add_picture(
4450                composite_mode,
4451                clip_node_id,
4452                Picture3DContext::Out,
4453                &mut self.interners,
4454                &mut self.prim_store,
4455                &mut self.prim_instances,
4456                &mut self.clip_tree_builder,
4457            );
4458
4459            return source;
4460        }
4461
4462        for filter in &mut filter_ops {
4464            let composite_mode = match filter {
4465                Filter::ComponentTransfer => {
4466                    let filter_data =
4467                        &filter_datas[current_filter_data_index];
4468                    let filter_data = filter_data.sanitize();
4469                    current_filter_data_index = current_filter_data_index + 1;
4470                    if filter_data.is_identity() {
4471                        continue
4472                    } else {
4473                        let filter_data_key = SFilterDataKey {
4474                            data:
4475                                SFilterData {
4476                                    r_func: SFilterDataComponent::from_functype_values(
4477                                        filter_data.func_r_type, &filter_data.r_values),
4478                                    g_func: SFilterDataComponent::from_functype_values(
4479                                        filter_data.func_g_type, &filter_data.g_values),
4480                                    b_func: SFilterDataComponent::from_functype_values(
4481                                        filter_data.func_b_type, &filter_data.b_values),
4482                                    a_func: SFilterDataComponent::from_functype_values(
4483                                        filter_data.func_a_type, &filter_data.a_values),
4484                                },
4485                        };
4486
4487                        let handle = self.interners
4488                            .filter_data
4489                            .intern(&filter_data_key, || ());
4490                        PictureCompositeMode::ComponentTransferFilter(handle)
4491                    }
4492                }
4493                Filter::SVGGraphNode(_, _) => {
4494                    panic!("SVGGraphNode encountered in regular CSS filter chain?");
4496                }
4497                _ => {
4498                    if filter.is_noop() {
4499                        continue;
4500                    } else {
4501                        let mut filter = filter.clone();
4502
4503                        if is_backdrop_filter {
4507                            if let Filter::Blur { ref mut should_inflate, ref mut edge_mode, .. } = filter {
4508                                *should_inflate = false;
4509                                *edge_mode = BlurEdgeMode::Mirror;
4510                            }
4511                        }
4512
4513                        PictureCompositeMode::Filter(filter)
4514                    }
4515                }
4516            };
4517
4518            source = source.add_picture(
4519                composite_mode,
4520                clip_node_id,
4521                Picture3DContext::Out,
4522                &mut self.interners,
4523                &mut self.prim_store,
4524                &mut self.prim_instances,
4525                &mut self.clip_tree_builder,
4526            );
4527        }
4528
4529        if !filter_primitives.is_empty() {
4530            let filter_datas = filter_datas.iter()
4531                .map(|filter_data| filter_data.sanitize())
4532                .map(|filter_data| {
4533                    SFilterData {
4534                        r_func: SFilterDataComponent::from_functype_values(
4535                            filter_data.func_r_type, &filter_data.r_values),
4536                        g_func: SFilterDataComponent::from_functype_values(
4537                            filter_data.func_g_type, &filter_data.g_values),
4538                        b_func: SFilterDataComponent::from_functype_values(
4539                            filter_data.func_b_type, &filter_data.b_values),
4540                        a_func: SFilterDataComponent::from_functype_values(
4541                            filter_data.func_a_type, &filter_data.a_values),
4542                    }
4543                })
4544                .collect();
4545
4546            for primitive in &mut filter_primitives {
4548                primitive.sanitize();
4549            }
4550
4551            let composite_mode = PictureCompositeMode::SvgFilter(
4552                filter_primitives,
4553                filter_datas,
4554            );
4555
4556            source = source.add_picture(
4557                composite_mode,
4558                clip_node_id,
4559                Picture3DContext::Out,
4560                &mut self.interners,
4561                &mut self.prim_store,
4562                &mut self.prim_instances,
4563                &mut self.clip_tree_builder,
4564            );
4565        }
4566
4567        source
4568    }
4569}
4570
4571
4572pub trait CreateShadow {
4573    fn create_shadow(
4574        &self,
4575        shadow: &Shadow,
4576        blur_is_noop: bool,
4577        current_raster_space: RasterSpace,
4578    ) -> Self;
4579}
4580
4581pub trait IsVisible {
4582    fn is_visible(&self) -> bool;
4583}
4584
4585struct ExtendedPrimitiveInstance {
4589    instance: PrimitiveInstance,
4590    spatial_node_index: SpatialNodeIndex,
4591    flags: PrimitiveFlags,
4592}
4593
4594struct StackingContextInfo {
4597    pop_containing_block: bool,
4599    pop_stacking_context: bool,
4601    set_tile_cache_barrier: bool,
4603    needs_extra_stacking_context: bool,
4607}
4608
4609struct FlattenedStackingContext {
4613    prim_list: PrimitiveList,
4615
4616    prim_flags: PrimitiveFlags,
4618
4619    spatial_node_index: SpatialNodeIndex,
4621
4622    clip_node_id: ClipNodeId,
4624
4625    composite_ops: CompositeOps,
4628
4629    blit_reason: BlitReason,
4632
4633    transform_style: TransformStyle,
4635
4636    context_3d: Picture3DContext<ExtendedPrimitiveInstance>,
4638
4639    flags: StackingContextFlags,
4641
4642    raster_space: RasterSpace,
4644
4645    subregion_offset: LayoutVector2D,
4647}
4648
4649impl FlattenedStackingContext {
4650    pub fn is_3d(&self) -> bool {
4652        self.transform_style == TransformStyle::Preserve3D && self.composite_ops.is_empty()
4653    }
4654
4655    pub fn is_redundant(
4657        context_3d: &Picture3DContext<ExtendedPrimitiveInstance>,
4658        composite_ops: &CompositeOps,
4659        blit_reason: BlitReason,
4660        parent: Option<&FlattenedStackingContext>,
4661        prim_flags: PrimitiveFlags,
4662    ) -> bool {
4663        if let Picture3DContext::In { .. } = context_3d {
4665            return false;
4666        }
4667
4668        if composite_ops.has_valid_filters() {
4670            return false;
4671        }
4672
4673        if composite_ops.mix_blend_mode.is_some() {
4675            match parent {
4676                Some(ref parent) => {
4677                    if !parent.prim_list.is_empty() {
4680                        return false;
4681                    }
4682                }
4683                None => {
4684                    return false;
4688                }
4689            }
4690        }
4691
4692        if !blit_reason.is_empty() {
4694            return false;
4695        }
4696
4697        if !prim_flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE) {
4699            return false;
4700        }
4701
4702        true
4704    }
4705
4706    pub fn cut_item_sequence(
4708        &mut self,
4709        prim_store: &mut PrimitiveStore,
4710        interners: &mut Interners,
4711        composite_mode: Option<PictureCompositeMode>,
4712        flat_items_context_3d: Picture3DContext<OrderedPictureChild>,
4713        clip_tree_builder: &mut ClipTreeBuilder,
4714    ) -> Option<(PictureIndex, PrimitiveInstance)> {
4715        if self.prim_list.is_empty() {
4716            return None
4717        }
4718
4719        let pic_index = PictureIndex(prim_store.pictures
4720            .alloc()
4721            .init(PicturePrimitive::new_image(
4722                composite_mode.clone(),
4723                flat_items_context_3d,
4724                self.prim_flags,
4725                mem::replace(&mut self.prim_list, PrimitiveList::empty()),
4726                self.spatial_node_index,
4727                self.raster_space,
4728                PictureFlags::empty(),
4729                None
4730            ))
4731        );
4732
4733        let prim_instance = create_prim_instance(
4734            pic_index,
4735            composite_mode.into(),
4736            self.raster_space,
4737            self.clip_node_id,
4738            interners,
4739            clip_tree_builder,
4740        );
4741
4742        Some((pic_index, prim_instance))
4743    }
4744}
4745
4746pub struct PendingPrimitive<T> {
4750    spatial_node_index: SpatialNodeIndex,
4751    clip_node_id: ClipNodeId,
4752    info: LayoutPrimitiveInfo,
4753    prim: T,
4754}
4755
4756pub struct PendingShadow {
4759    shadow: Shadow,
4760    should_inflate: bool,
4761    spatial_node_index: SpatialNodeIndex,
4762}
4763
4764pub enum ShadowItem {
4765    Shadow(PendingShadow),
4766    Image(PendingPrimitive<Image>),
4767    LineDecoration(PendingPrimitive<LineDecoration>),
4768    NormalBorder(PendingPrimitive<NormalBorderPrim>),
4769    Primitive(PendingPrimitive<PrimitiveKeyKind>),
4770    TextRun(PendingPrimitive<TextRun>),
4771}
4772
4773impl From<PendingPrimitive<Image>> for ShadowItem {
4774    fn from(image: PendingPrimitive<Image>) -> Self {
4775        ShadowItem::Image(image)
4776    }
4777}
4778
4779impl From<PendingPrimitive<LineDecoration>> for ShadowItem {
4780    fn from(line_dec: PendingPrimitive<LineDecoration>) -> Self {
4781        ShadowItem::LineDecoration(line_dec)
4782    }
4783}
4784
4785impl From<PendingPrimitive<NormalBorderPrim>> for ShadowItem {
4786    fn from(border: PendingPrimitive<NormalBorderPrim>) -> Self {
4787        ShadowItem::NormalBorder(border)
4788    }
4789}
4790
4791impl From<PendingPrimitive<PrimitiveKeyKind>> for ShadowItem {
4792    fn from(container: PendingPrimitive<PrimitiveKeyKind>) -> Self {
4793        ShadowItem::Primitive(container)
4794    }
4795}
4796
4797impl From<PendingPrimitive<TextRun>> for ShadowItem {
4798    fn from(text_run: PendingPrimitive<TextRun>) -> Self {
4799        ShadowItem::TextRun(text_run)
4800    }
4801}
4802
4803fn create_prim_instance(
4804    pic_index: PictureIndex,
4805    composite_mode_key: PictureCompositeKey,
4806    raster_space: RasterSpace,
4807    clip_node_id: ClipNodeId,
4808    interners: &mut Interners,
4809    clip_tree_builder: &mut ClipTreeBuilder,
4810) -> PrimitiveInstance {
4811    let pic_key = PictureKey::new(
4812        Picture {
4813            composite_mode_key,
4814            raster_space,
4815        },
4816    );
4817
4818    let data_handle = interners
4819        .picture
4820        .intern(&pic_key, || ());
4821
4822    PrimitiveInstance::new(
4823        PrimitiveInstanceKind::Picture {
4824            data_handle,
4825            pic_index,
4826        },
4827        clip_tree_builder.build_for_picture(
4828            clip_node_id,
4829        ),
4830    )
4831}
4832
4833fn filter_ops_for_compositing(
4834    input_filters: ItemRange<FilterOp>,
4835) -> Vec<Filter> {
4836    input_filters.iter().map(|filter| filter.into()).collect()
4840}
4841
4842fn filter_datas_for_compositing(
4843    input_filter_datas: &[TempFilterData],
4844) -> Vec<FilterData> {
4845    let mut filter_datas = vec![];
4849    for temp_filter_data in input_filter_datas {
4850        let func_types : Vec<ComponentTransferFuncType> = temp_filter_data.func_types.iter().collect();
4851        debug_assert!(func_types.len() == 4);
4852        filter_datas.push( FilterData {
4853            func_r_type: func_types[0],
4854            r_values: temp_filter_data.r_values.iter().collect(),
4855            func_g_type: func_types[1],
4856            g_values: temp_filter_data.g_values.iter().collect(),
4857            func_b_type: func_types[2],
4858            b_values: temp_filter_data.b_values.iter().collect(),
4859            func_a_type: func_types[3],
4860            a_values: temp_filter_data.a_values.iter().collect(),
4861        });
4862    }
4863    filter_datas
4864}
4865
4866fn filter_primitives_for_compositing(
4867    input_filter_primitives: ItemRange<FilterPrimitive>,
4868) -> Vec<FilterPrimitive> {
4869    input_filter_primitives.iter().map(|primitive| primitive).collect()
4874}
4875
4876fn process_repeat_size(
4877    snapped_rect: &LayoutRect,
4878    unsnapped_rect: &LayoutRect,
4879    repeat_size: LayoutSize,
4880) -> LayoutSize {
4881    const EPSILON: f32 = 0.001;
4889    LayoutSize::new(
4890        if repeat_size.width.approx_eq_eps(&unsnapped_rect.width(), &EPSILON) {
4891            snapped_rect.width()
4892        } else {
4893            repeat_size.width
4894        },
4895        if repeat_size.height.approx_eq_eps(&unsnapped_rect.height(), &EPSILON) {
4896            snapped_rect.height()
4897        } else {
4898            repeat_size.height
4899        },
4900    )
4901}
4902
4903fn read_gradient_stops(stops: ItemRange<GradientStop>) -> Vec<GradientStopKey> {
4904    stops.iter().map(|stop| {
4905        GradientStopKey {
4906            offset: stop.offset,
4907            color: stop.color.into(),
4908        }
4909    }).collect()
4910}
4911
4912pub struct SceneRecycler {
4916    pub tx: Sender<BuiltScene>,
4917    rx: Receiver<BuiltScene>,
4918
4919    pub prim_store: PrimitiveStore,
4922    pub clip_store: ClipStore,
4923    pub picture_graph: PictureGraph,
4924    pub prim_instances: Vec<PrimitiveInstance>,
4925    pub surfaces: Vec<SurfaceInfo>,
4926    pub hit_testing_scene: Option<HitTestingScene>,
4927    pub clip_tree_builder: Option<ClipTreeBuilder>,
4928    id_to_index_mapper_stack: Vec<NodeIdToIndexMapper>,
4937    sc_stack: Vec<FlattenedStackingContext>,
4938    containing_block_stack: Vec<SpatialNodeIndex>,
4939    raster_space_stack: Vec<RasterSpace>,
4940    pending_shadow_items: VecDeque<ShadowItem>,
4941    iframe_size: Vec<LayoutSize>,
4942}
4943
4944impl SceneRecycler {
4945    pub fn new() -> Self {
4946        let (tx, rx) = unbounded_channel();
4947        SceneRecycler {
4948            tx,
4949            rx,
4950
4951            prim_instances: Vec::new(),
4952            surfaces: Vec::new(),
4953            prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()),
4954            clip_store: ClipStore::new(),
4955            picture_graph: PictureGraph::new(),
4956            hit_testing_scene: None,
4957            clip_tree_builder: None,
4958
4959            id_to_index_mapper_stack: Vec::new(),
4960            sc_stack: Vec::new(),
4961            containing_block_stack: Vec::new(),
4962            raster_space_stack: Vec::new(),
4963            pending_shadow_items: VecDeque::new(),
4964            iframe_size: Vec::new(),
4965        }
4966    }
4967
4968    #[inline(never)]
4974    pub fn recycle_built_scene(&mut self) {
4975        let Ok(scene) = self.rx.try_recv() else {
4976            return;
4977        };
4978
4979        self.prim_store = scene.prim_store;
4980        self.clip_store = scene.clip_store;
4981        self.prim_store.reset();
4985        self.clip_store.reset();
4986        self.hit_testing_scene = Arc::try_unwrap(scene.hit_testing_scene).ok();
4987        self.picture_graph = scene.picture_graph;
4988        self.prim_instances = scene.prim_instances;
4989        self.surfaces = scene.surfaces;
4990        if let Some(clip_tree_builder) = &mut self.clip_tree_builder {
4991            clip_tree_builder.recycle_tree(scene.clip_tree);
4992        }
4993
4994        while let Ok(_) = self.rx.try_recv() {
4995            }
4998
4999        }
5001}