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