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, 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, 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, ClipItemEntry, ClipStore};
54use crate::clip::{ClipInternData, ClipNodeId, ClipLeafId};
55use crate::clip::{PolygonDataHandle, ClipTreeBuilder};
56use crate::gpu_types::BlurEdgeMode;
57use crate::segment::EdgeMask;
58use crate::spatial_tree::{SceneSpatialTree, SpatialNodeContainer, SpatialNodeIndex, get_external_scroll_offset};
59use crate::frame_builder::FrameBuilderConfig;
60use glyph_rasterizer::{FontInstance, SharedFontResources};
61use crate::hit_test::HitTestingScene;
62use crate::intern::Interner;
63use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter, PlaneSplitterIndex};
64use crate::svg_filter::{FilterGraphNode, FilterGraphOp, FilterGraphPictureReference};
65use crate::picture::{Picture3DContext, PictureCompositeMode, PictureInstance};
66use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList, SurfaceInfo, PictureFlags};
67use crate::picture_graph::PictureGraph;
68use crate::prim_store::{PrimitiveInstance, PrimitiveStoreStats};
69use crate::prim_store::{PrimitiveKind, NinePatchDescriptor, PrimitiveStore};
70use crate::prim_store::{InternablePrimitive, PictureIndex};
71use crate::prim_store::PolygonKey;
72use crate::prim_store::rectangle::RectanglePrim;
73use crate::prim_store::backdrop::{BackdropCapture, BackdropRender};
74use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
75use crate::prim_store::gradient::{
76 GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient,
77 ConicGradientParams, optimize_radial_gradient, apply_gradient_local_clip,
78 optimize_linear_gradient,
79};
80use crate::prim_store::image::{Image, StretchSizeKey, YuvImage};
81use crate::prim_store::line_dec::LineDecoration;
82use crate::prim_store::picture::{Picture, PictureKey};
83use crate::picture_composite_mode::PictureCompositeKey;
84use crate::prim_store::text_run::TextRun;
85use crate::render_backend::SceneView;
86use crate::resource_cache::ImageRequest;
87use crate::scene::{BuiltScene, Scene, ScenePipeline, SceneStats, StackingContextHelpers};
88use crate::scene_builder_thread::Interners;
89use crate::space::SpaceSnapper;
90use crate::spatial_node::{
91 ReferenceFrameInfo, StickyFrameInfo, ScrollFrameKind, SpatialNodeType
92};
93use crate::tile_cache::TileCacheBuilder;
94use euclid::approxeq::ApproxEq;
95use std::{f32, mem, usize};
96use std::collections::vec_deque::VecDeque;
97use std::sync::Arc;
98use crate::util::{VecHelper, MaxRect};
99use crate::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
100use log::Level;
101
102pub struct ScrollOffsetMapper {
105 pub current_spatial_node: SpatialNodeIndex,
106 pub current_offset: LayoutVector2D,
107}
108
109impl ScrollOffsetMapper {
110 fn new() -> Self {
111 ScrollOffsetMapper {
112 current_spatial_node: SpatialNodeIndex::INVALID,
113 current_offset: LayoutVector2D::zero(),
114 }
115 }
116
117 fn external_scroll_offset(
121 &mut self,
122 spatial_node_index: SpatialNodeIndex,
123 spatial_tree: &SceneSpatialTree,
124 ) -> LayoutVector2D {
125 if spatial_node_index != self.current_spatial_node {
126 self.current_spatial_node = spatial_node_index;
127 self.current_offset = get_external_scroll_offset(spatial_tree, spatial_node_index);
128 }
129
130 self.current_offset
131 }
132}
133
134#[derive(Default)]
138pub struct NodeIdToIndexMapper {
139 spatial_node_map: FastHashMap<SpatialId, SpatialNodeIndex>,
140}
141
142impl NodeIdToIndexMapper {
143 fn add_spatial_node(&mut self, id: SpatialId, index: SpatialNodeIndex) {
144 let _old_value = self.spatial_node_map.insert(id, index);
145 assert!(_old_value.is_none());
146 }
147
148 fn get_spatial_node_index(&self, id: SpatialId) -> SpatialNodeIndex {
149 self.spatial_node_map[&id]
150 }
151}
152
153#[derive(Debug, Clone, Default)]
154pub struct CompositeOps {
155 pub filters: Vec<Filter>,
157 pub filter_datas: Vec<FilterData>,
158 pub snapshot: Option<SnapshotInfo>,
159
160 pub mix_blend_mode: Option<MixBlendMode>,
162}
163
164impl CompositeOps {
165 pub fn new(
166 filters: Vec<Filter>,
167 filter_datas: Vec<FilterData>,
168 mix_blend_mode: Option<MixBlendMode>,
169 snapshot: Option<SnapshotInfo>,
170 ) -> Self {
171 CompositeOps {
172 filters,
173 filter_datas,
174 mix_blend_mode,
175 snapshot,
176 }
177 }
178
179 pub fn is_empty(&self) -> bool {
180 self.filters.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 false
215 }
216}
217
218enum PictureSource {
221 PrimitiveList {
222 prim_list: PrimitiveList,
223 },
224 WrappedPicture {
225 instance: PrimitiveInstance,
226 },
227}
228
229struct PictureChainBuilder {
232 current: PictureSource,
234
235 spatial_node_index: SpatialNodeIndex,
237 flags: PrimitiveFlags,
239 raster_space: RasterSpace,
241 set_resolve_target: bool,
243 establishes_sub_graph: bool,
245}
246
247impl PictureChainBuilder {
248 fn from_prim_list(
250 prim_list: PrimitiveList,
251 flags: PrimitiveFlags,
252 spatial_node_index: SpatialNodeIndex,
253 raster_space: RasterSpace,
254 is_sub_graph: bool,
255 ) -> Self {
256 PictureChainBuilder {
257 current: PictureSource::PrimitiveList {
258 prim_list,
259 },
260 spatial_node_index,
261 flags,
262 raster_space,
263 establishes_sub_graph: is_sub_graph,
264 set_resolve_target: is_sub_graph,
265 }
266 }
267
268 fn from_instance(
270 instance: PrimitiveInstance,
271 flags: PrimitiveFlags,
272 spatial_node_index: SpatialNodeIndex,
273 raster_space: RasterSpace,
274 ) -> Self {
275 PictureChainBuilder {
276 current: PictureSource::WrappedPicture {
277 instance,
278 },
279 flags,
280 spatial_node_index,
281 raster_space,
282 establishes_sub_graph: false,
283 set_resolve_target: false,
284 }
285 }
286
287 #[must_use]
289 fn add_picture(
290 self,
291 composite_mode: PictureCompositeMode,
292 clip_node_id: ClipNodeId,
293 context_3d: Picture3DContext<OrderedPictureChild>,
294 interners: &mut Interners,
295 prim_store: &mut PrimitiveStore,
296 prim_instances: &mut Vec<PrimitiveInstance>,
297 clip_tree_builder: &mut ClipTreeBuilder,
298 ) -> PictureChainBuilder {
299 let prim_list = match self.current {
300 PictureSource::PrimitiveList { prim_list } => {
301 prim_list
302 }
303 PictureSource::WrappedPicture { instance } => {
304 let mut prim_list = PrimitiveList::empty();
305
306 prim_list.add_prim(
307 instance,
308 LayoutRect::zero(),
309 self.spatial_node_index,
310 self.flags,
311 prim_instances,
312 clip_tree_builder,
313 );
314
315 prim_list
316 }
317 };
318
319 let flags = if self.set_resolve_target {
320 PictureFlags::IS_RESOLVE_TARGET
321 } else {
322 PictureFlags::empty()
323 };
324
325 let pic_index = PictureIndex(prim_store.pictures
326 .alloc()
327 .init(PictureInstance::new_image(
328 Some(composite_mode.clone()),
329 context_3d,
330 self.flags,
331 prim_list,
332 self.spatial_node_index,
333 self.raster_space,
334 flags,
335 None,
336 ))
337 );
338
339 let instance = create_prim_instance(
340 pic_index,
341 Some(composite_mode).into(),
342 self.raster_space,
343 clip_node_id,
344 interners,
345 clip_tree_builder,
346 );
347
348 PictureChainBuilder {
349 current: PictureSource::WrappedPicture {
350 instance,
351 },
352 spatial_node_index: self.spatial_node_index,
353 flags: self.flags,
354 raster_space: self.raster_space,
355 set_resolve_target: false,
357 establishes_sub_graph: self.establishes_sub_graph,
358 }
359 }
360
361 fn finalize(
363 self,
364 clip_node_id: ClipNodeId,
365 interners: &mut Interners,
366 prim_store: &mut PrimitiveStore,
367 clip_tree_builder: &mut ClipTreeBuilder,
368 snapshot: Option<SnapshotInfo>,
369 ) -> PrimitiveInstance {
370 let mut flags = PictureFlags::empty();
371 if self.establishes_sub_graph {
372 flags |= PictureFlags::IS_SUB_GRAPH;
373 }
374
375 match self.current {
376 PictureSource::WrappedPicture { instance } => {
377 let pic_index = instance.kind.as_pic();
378 let picture = &mut prim_store.pictures[pic_index.0];
379 picture.flags |= flags;
380 picture.snapshot = snapshot;
381
382 instance
383 }
384 PictureSource::PrimitiveList { prim_list } => {
385 if self.set_resolve_target {
386 flags |= PictureFlags::IS_RESOLVE_TARGET;
387 }
388
389 let composite_mode = snapshot.map(|_| PictureCompositeMode::Blit(BlitReason::SNAPSHOT));
396
397 let pic_index = PictureIndex(prim_store.pictures
398 .alloc()
399 .init(PictureInstance::new_image(
400 composite_mode,
401 Picture3DContext::Out,
402 self.flags,
403 prim_list,
404 self.spatial_node_index,
405 self.raster_space,
406 flags,
407 snapshot,
408 ))
409 );
410
411 create_prim_instance(
412 pic_index,
413 None.into(),
414 self.raster_space,
415 clip_node_id,
416 interners,
417 clip_tree_builder,
418 )
419 }
420 }
421 }
422
423 #[allow(dead_code)]
425 fn has_picture(&self) -> bool {
426 match self.current {
427 PictureSource::WrappedPicture { .. } => true,
428 PictureSource::PrimitiveList { .. } => false,
429 }
430 }
431}
432
433bitflags! {
434 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
436 pub struct SliceFlags : u8 {
437 const IS_SCROLLBAR = 1;
439 const IS_ATOMIC = 2;
441 }
442}
443
444pub struct SceneBuilder<'a> {
448 scene: &'a Scene,
450
451 fonts: SharedFontResources,
453
454 id_to_index_mapper_stack: Vec<NodeIdToIndexMapper>,
457
458 sc_stack: Vec<FlattenedStackingContext>,
460
461 containing_block_stack: Vec<SpatialNodeIndex>,
463
464 raster_space_stack: Vec<RasterSpace>,
466
467 pending_shadow_items: VecDeque<ShadowItem>,
469
470 pub spatial_tree: &'a mut SceneSpatialTree,
472
473 pub prim_store: PrimitiveStore,
475
476 pub hit_testing_scene: HitTestingScene,
478
479 pub clip_store: ClipStore,
481
482 pub config: FrameBuilderConfig,
485
486 pub interners: &'a mut Interners,
488
489 external_scroll_mapper: ScrollOffsetMapper,
491
492 iframe_size: Vec<LayoutSize>,
495
496 root_iframe_clip: Option<ClipId>,
498
499 quality_settings: QualitySettings,
501
502 tile_cache_builder: TileCacheBuilder,
504
505 picture_graph: PictureGraph,
510
511 snapshot_pictures: Vec<PictureIndex>,
514
515 next_plane_splitter_index: usize,
523
524 prim_instances: Vec<PrimitiveInstance>,
528
529
530 surfaces: Vec<SurfaceInfo>,
534
535 clip_tree_builder: ClipTreeBuilder,
537
538 extra_stacking_context_stack: Vec<StackingContextInfo>,
543}
544
545impl<'a> SceneBuilder<'a> {
546 pub fn build(
547 scene: &Scene,
548 root_pipeline: Option<PipelineId>,
549 fonts: SharedFontResources,
550 view: &SceneView,
551 frame_builder_config: &FrameBuilderConfig,
552 interners: &mut Interners,
553 spatial_tree: &mut SceneSpatialTree,
554 recycler: &mut SceneRecycler,
555 stats: &SceneStats,
556 debug_flags: DebugFlags,
557 ) -> BuiltScene {
558 profile_scope!("build_scene");
559
560 let root_pipeline_id = root_pipeline.or(scene.root_pipeline_id).unwrap();
562 let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
563
564 spatial_tree.reset();
567 let root_reference_frame_index = spatial_tree.root_reference_frame_index();
568
569 let mut builder = SceneBuilder {
570 scene,
571 spatial_tree,
572 fonts,
573 config: *frame_builder_config,
574 id_to_index_mapper_stack: mem::take(&mut recycler.id_to_index_mapper_stack),
575 hit_testing_scene: recycler.hit_testing_scene.take().unwrap_or_else(|| HitTestingScene::new(&stats.hit_test_stats)),
576 pending_shadow_items: mem::take(&mut recycler.pending_shadow_items),
577 sc_stack: mem::take(&mut recycler.sc_stack),
578 containing_block_stack: mem::take(&mut recycler.containing_block_stack),
579 raster_space_stack: mem::take(&mut recycler.raster_space_stack),
580 prim_store: mem::take(&mut recycler.prim_store),
581 clip_store: mem::take(&mut recycler.clip_store),
582 interners,
583 external_scroll_mapper: ScrollOffsetMapper::new(),
584 iframe_size: mem::take(&mut recycler.iframe_size),
585 root_iframe_clip: None,
586 quality_settings: view.quality_settings,
587 tile_cache_builder: TileCacheBuilder::new(
588 root_reference_frame_index,
589 frame_builder_config.background_color,
590 debug_flags,
591 ),
592 picture_graph: mem::take(&mut recycler.picture_graph),
593 snapshot_pictures: Vec::new(),
595 next_plane_splitter_index: 0,
596 prim_instances: mem::take(&mut recycler.prim_instances),
597 surfaces: mem::take(&mut recycler.surfaces),
598 clip_tree_builder: recycler.clip_tree_builder.take().unwrap_or_else(|| ClipTreeBuilder::new()),
599 extra_stacking_context_stack: Vec::new(),
600 };
601
602 builder.hit_testing_scene.reset();
604 builder.prim_store.reset();
605 builder.clip_store.reset();
606 builder.picture_graph.reset();
607 builder.prim_instances.clear();
608 builder.surfaces.clear();
609 builder.sc_stack.clear();
610 builder.containing_block_stack.clear();
611 builder.id_to_index_mapper_stack.clear();
612 builder.pending_shadow_items.clear();
613 builder.iframe_size.clear();
614
615 builder.raster_space_stack.clear();
616 builder.raster_space_stack.push(RasterSpace::Screen);
617
618 builder.clip_tree_builder.begin();
619
620 builder.build_all(
621 root_pipeline_id,
622 &root_pipeline,
623 );
624
625 let (tile_cache_config, tile_cache_pictures) = builder.tile_cache_builder.build(
627 &builder.config,
628 &mut builder.prim_store,
629 &builder.spatial_tree,
630 &builder.prim_instances,
631 &mut builder.clip_tree_builder,
632 &builder.interners,
633 );
634
635 for pic_index in &builder.snapshot_pictures {
636 builder.picture_graph.add_root(*pic_index);
637 }
638
639 for pic_index in &tile_cache_pictures {
641 builder.picture_graph.add_root(*pic_index);
642 SceneBuilder::finalize_picture(
643 *pic_index,
644 None,
645 &mut builder.prim_store.pictures,
646 None,
647 &builder.clip_tree_builder,
648 &builder.prim_instances,
649 &builder.interners.clip,
650 );
651 }
652
653 let clip_tree = builder.clip_tree_builder.finalize();
654
655 recycler.clip_tree_builder = Some(builder.clip_tree_builder);
656 recycler.sc_stack = builder.sc_stack;
657 recycler.id_to_index_mapper_stack = builder.id_to_index_mapper_stack;
658 recycler.containing_block_stack = builder.containing_block_stack;
659 recycler.raster_space_stack = builder.raster_space_stack;
660 recycler.pending_shadow_items = builder.pending_shadow_items;
661 recycler.iframe_size = builder.iframe_size;
662
663 BuiltScene {
664 has_root_pipeline: scene.has_root_pipeline(),
665 pipeline_epochs: scene.pipeline_epochs.clone(),
666 output_rect: view.device_rect.size().into(),
667 hit_testing_scene: Arc::new(builder.hit_testing_scene),
668 prim_store: builder.prim_store,
669 clip_store: builder.clip_store,
670 config: builder.config,
671 tile_cache_config,
672 snapshot_pictures: builder.snapshot_pictures,
673 tile_cache_pictures,
674 picture_graph: builder.picture_graph,
675 num_plane_splitters: builder.next_plane_splitter_index,
676 prim_instances: builder.prim_instances,
677 surfaces: builder.surfaces,
678 clip_tree,
679 recycler_tx: Some(recycler.tx.clone()),
680 }
681 }
682
683 fn finalize_picture(
691 pic_index: PictureIndex,
692 prim_index: Option<usize>,
693 pictures: &mut [PictureInstance],
694 parent_spatial_node_index: Option<SpatialNodeIndex>,
695 clip_tree_builder: &ClipTreeBuilder,
696 prim_instances: &[PrimitiveInstance],
697 clip_interner: &Interner<ClipIntern>,
698 ) {
699 let (mut prim_list, spatial_node_index) = {
702 let pic = &mut pictures[pic_index.0];
703 assert_ne!(pic.spatial_node_index, SpatialNodeIndex::UNKNOWN);
704
705 if pic.flags.contains(PictureFlags::IS_RESOLVE_TARGET) {
706 pic.flags |= PictureFlags::DISABLE_SNAPPING;
707 }
708
709 let spatial_node_index = match pic.composite_mode {
711 Some(_) => pic.spatial_node_index,
712 None => parent_spatial_node_index.expect("bug: no parent"),
713 };
714
715 (
716 mem::replace(&mut pic.prim_list, PrimitiveList::empty()),
717 spatial_node_index,
718 )
719 };
720
721 for cluster in &mut prim_list.clusters {
723 if cluster.spatial_node_index == SpatialNodeIndex::UNKNOWN {
724 cluster.spatial_node_index = spatial_node_index;
725 }
726 }
727
728 let mut shared_clip_node_id = None;
737
738 let is_snapshot = pictures[pic_index.0].snapshot.is_some();
743
744 if is_snapshot {
745 if let Some(idx) = prim_index {
756 let clip_node = clip_tree_builder.get_leaf(prim_instances[idx].clip_leaf_id).node_id;
757 shared_clip_node_id = clip_tree_builder.get_parent(clip_node);
758 }
759 } else {
760 for cluster in &prim_list.clusters {
761 for prim_instance in &prim_instances[cluster.prim_range()] {
762 let leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
763
764 shared_clip_node_id = match shared_clip_node_id {
765 Some(current) => {
766 Some(clip_tree_builder.find_lowest_common_ancestor(
767 current,
768 leaf.node_id,
769 ))
770 }
771 None => Some(leaf.node_id)
772 };
773 }
774 }
775 }
776
777 let lca_tree_node = shared_clip_node_id
784 .and_then(|node_id| (node_id != ClipNodeId::NONE).then_some(node_id))
785 .map(|node_id| clip_tree_builder.get_node(node_id));
786 let lca_node = lca_tree_node
787 .map(|tree_node| &clip_interner[tree_node.handle]);
788 let lca_clip_rect = lca_tree_node
789 .map(|tree_node| tree_node.unsnapped_clip_rect);
790 let pic_node_id = prim_index
791 .map(|prim_index| clip_tree_builder.get_leaf(prim_instances[prim_index].clip_leaf_id).node_id)
792 .and_then(|node_id| (node_id != ClipNodeId::NONE).then_some(node_id));
793 let pic_tree_node = pic_node_id
794 .map(|node_id| clip_tree_builder.get_node(node_id));
795 let pic_node = pic_tree_node
796 .map(|tree_node| &clip_interner[tree_node.handle]);
797 let pic_clip_rect = pic_tree_node
798 .map(|tree_node| tree_node.unsnapped_clip_rect);
799
800 let has_blur = match &pictures[pic_index.0].composite_mode {
806 Some(PictureCompositeMode::Filter(Filter::Blur { .. })) => true,
807 Some(PictureCompositeMode::Filter(Filter::DropShadows { .. })) => true,
808 Some(PictureCompositeMode::SVGFEGraph( .. )) => true,
809 _ => false,
810 };
811
812 let direct_parent = lca_tree_node
818 .zip(pic_node_id)
819 .map(|(lca_tree_node, pic_node_id)| lca_tree_node.parent == pic_node_id)
820 .unwrap_or(false);
821
822 let should_set_clip_root = is_snapshot || lca_node.zip(pic_node).map_or(false, |(lca_node, pic_node)| {
823 lca_node.key == pic_node.key &&
832 lca_clip_rect == pic_clip_rect &&
833 !has_blur && direct_parent
834 });
835
836 if should_set_clip_root {
837 pictures[pic_index.0].clip_root = shared_clip_node_id;
838 }
839
840 for cluster in &prim_list.clusters {
842 for prim_instance_index in cluster.prim_range() {
843 if let PrimitiveKind::Picture { pic_index: child_pic_index, .. } = prim_instances[prim_instance_index].kind {
844 let child_pic = &mut pictures[child_pic_index.0];
845
846 if child_pic.spatial_node_index == SpatialNodeIndex::UNKNOWN {
847 child_pic.spatial_node_index = spatial_node_index;
848 }
849
850 SceneBuilder::finalize_picture(
852 child_pic_index,
853 Some(prim_instance_index),
854 pictures,
855 Some(spatial_node_index),
856 clip_tree_builder,
857 prim_instances,
858 clip_interner,
859 );
860
861 if pictures[child_pic_index.0].flags.contains(PictureFlags::DISABLE_SNAPPING) {
862 pictures[pic_index.0].flags |= PictureFlags::DISABLE_SNAPPING;
863 }
864 }
865 }
866 }
867
868 pictures[pic_index.0].prim_list = prim_list;
870 }
871
872 fn current_external_scroll_offset(
874 &mut self,
875 spatial_node_index: SpatialNodeIndex,
876 ) -> LayoutVector2D {
877 self.external_scroll_mapper
879 .external_scroll_offset(
880 spatial_node_index,
881 self.spatial_tree,
882 )
883 }
884
885 fn build_spatial_tree_for_display_list(
886 &mut self,
887 dl: &BuiltDisplayList,
888 pipeline_id: PipelineId,
889 ) {
890 dl.iter_spatial_tree(|item| {
891 match item {
892 SpatialTreeItem::ScrollFrame(descriptor) => {
893 let parent_space = self.get_space(descriptor.parent_space);
894 self.build_scroll_frame(
895 descriptor,
896 parent_space,
897 pipeline_id,
898 );
899 }
900 SpatialTreeItem::ReferenceFrame(descriptor) => {
901 let parent_space = self.get_space(descriptor.parent_spatial_id);
902 self.build_reference_frame(
903 descriptor,
904 parent_space,
905 pipeline_id,
906 );
907 }
908 SpatialTreeItem::StickyFrame(descriptor) => {
909 let parent_space = self.get_space(descriptor.parent_spatial_id);
910 self.build_sticky_frame(
911 descriptor,
912 parent_space,
913 );
914 }
915 SpatialTreeItem::Invalid => {
916 unreachable!();
917 }
918 }
919 });
920 }
921
922 fn build_all(
923 &mut self,
924 root_pipeline_id: PipelineId,
925 root_pipeline: &ScenePipeline,
926 ) {
927 enum ContextKind<'a> {
928 Root,
929 StackingContext {
930 sc_info: StackingContextInfo,
931 },
932 ReferenceFrame,
933 Iframe {
934 parent_traversal: BuiltDisplayListIter<'a>,
935 }
936 }
937 struct BuildContext<'a> {
938 pipeline_id: PipelineId,
939 kind: ContextKind<'a>,
940 }
941
942 self.id_to_index_mapper_stack.push(NodeIdToIndexMapper::default());
943
944 self.push_root(
945 root_pipeline_id,
946 );
947 self.build_spatial_tree_for_display_list(
948 &root_pipeline.display_list,
949 root_pipeline_id,
950 );
951
952 let mut stack = vec![BuildContext {
953 pipeline_id: root_pipeline_id,
954 kind: ContextKind::Root,
955 }];
956 let mut traversal = root_pipeline.display_list.iter();
957
958 'outer: while let Some(bc) = stack.pop() {
959 loop {
960 let item = match traversal.next() {
961 Some(item) => item,
962 None => break,
963 };
964
965 match item.item() {
966 DisplayItem::PushStackingContext(ref info) => {
967 profile_scope!("build_stacking_context");
968 let spatial_node_index = self.get_space(info.spatial_id);
969 let mut subtraversal = item.sub_iter();
970 if subtraversal.current_stacking_context_empty() && item.filters().is_empty() {
976 subtraversal.skip_current_stacking_context();
977 traversal = subtraversal;
978 continue;
979 }
980
981 let snapshot = info.snapshot;
982
983 let composition_operations = CompositeOps::new(
984 filter_ops_for_compositing(item.filters()),
985 filter_datas_for_compositing(item.filter_datas()),
986 info.stacking_context.mix_blend_mode_for_compositing(),
987 snapshot,
988 );
989
990 let sc_info = self.push_stacking_context(
991 composition_operations,
992 info.stacking_context.transform_style,
993 info.prim_flags,
994 spatial_node_index,
995 info.stacking_context.clip_chain_id,
996 info.stacking_context.raster_space,
997 info.stacking_context.flags,
998 );
999
1000 let new_context = BuildContext {
1001 pipeline_id: bc.pipeline_id,
1002 kind: ContextKind::StackingContext {
1003 sc_info,
1004 },
1005 };
1006 stack.push(bc);
1007 stack.push(new_context);
1008
1009 subtraversal.merge_debug_stats_from(&mut traversal);
1010 traversal = subtraversal;
1011 continue 'outer;
1012 }
1013 DisplayItem::PushReferenceFrame(..) => {
1014 profile_scope!("build_reference_frame");
1015 let mut subtraversal = item.sub_iter();
1016
1017 let new_context = BuildContext {
1018 pipeline_id: bc.pipeline_id,
1019 kind: ContextKind::ReferenceFrame,
1020 };
1021 stack.push(bc);
1022 stack.push(new_context);
1023
1024 subtraversal.merge_debug_stats_from(&mut traversal);
1025 traversal = subtraversal;
1026 continue 'outer;
1027 }
1028 DisplayItem::PopReferenceFrame |
1029 DisplayItem::PopStackingContext => break,
1030 DisplayItem::Iframe(ref info) => {
1031 profile_scope!("iframe");
1032
1033 let space = self.get_space(info.space_and_clip.spatial_id);
1034 let subtraversal = match self.push_iframe(info, space) {
1035 Some(pair) => pair,
1036 None => continue,
1037 };
1038
1039 let new_context = BuildContext {
1040 pipeline_id: info.pipeline_id,
1041 kind: ContextKind::Iframe {
1042 parent_traversal: mem::replace(&mut traversal, subtraversal),
1043 },
1044 };
1045 stack.push(bc);
1046 stack.push(new_context);
1047 continue 'outer;
1048 }
1049 _ => {
1050 self.build_item(item);
1051 }
1052 };
1053 }
1054
1055 match bc.kind {
1056 ContextKind::Root => {}
1057 ContextKind::StackingContext { sc_info } => {
1058 self.pop_stacking_context(sc_info);
1059 }
1060 ContextKind::ReferenceFrame => {
1061 }
1062 ContextKind::Iframe { parent_traversal } => {
1063 self.iframe_size.pop();
1064 self.clip_tree_builder.pop_clip();
1065 self.clip_tree_builder.pop_clip();
1066
1067 if self.iframe_size.is_empty() {
1068 assert!(self.root_iframe_clip.is_some());
1069 self.root_iframe_clip = None;
1070 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1071 }
1072
1073 self.id_to_index_mapper_stack.pop().unwrap();
1074
1075 traversal = parent_traversal;
1076 }
1077 }
1078
1079 if cfg!(feature = "display_list_stats") {
1081 let stats = traversal.debug_stats();
1082 let total_bytes: usize = stats.iter().map(|(_, stats)| stats.num_bytes).sum();
1083 debug!("item, total count, total bytes, % of DL bytes, bytes per item");
1084 for (label, stats) in stats {
1085 debug!("{}, {}, {}kb, {}%, {}",
1086 label,
1087 stats.total_count,
1088 stats.num_bytes / 1000,
1089 ((stats.num_bytes as f32 / total_bytes.max(1) as f32) * 100.0) as usize,
1090 stats.num_bytes / stats.total_count.max(1));
1091 }
1092 debug!("");
1093 }
1094 }
1095
1096 debug_assert!(self.sc_stack.is_empty());
1097
1098 self.id_to_index_mapper_stack.pop().unwrap();
1099 assert!(self.id_to_index_mapper_stack.is_empty());
1100 }
1101
1102 fn build_sticky_frame(
1103 &mut self,
1104 info: &StickyFrameDescriptor,
1105 parent_node_index: SpatialNodeIndex,
1106 ) {
1107 let external_scroll_offset = self.current_external_scroll_offset(parent_node_index);
1108
1109 let sticky_frame_info = StickyFrameInfo::new(
1110 info.bounds.translate(external_scroll_offset),
1111 info.margins,
1112 info.vertical_offset_bounds,
1113 info.horizontal_offset_bounds,
1114 info.previously_applied_offset,
1115 info.transform,
1116 );
1117
1118 let index = self.spatial_tree.add_sticky_frame(
1119 parent_node_index,
1120 sticky_frame_info,
1121 info.id.pipeline_id(),
1122 );
1123 self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(info.id, index);
1124 }
1125
1126 fn build_reference_frame(
1127 &mut self,
1128 info: &ReferenceFrameDescriptor,
1129 parent_space: SpatialNodeIndex,
1130 pipeline_id: PipelineId,
1131 ) {
1132 let transform = match info.reference_frame.transform {
1133 ReferenceTransformBinding::Static { binding } => binding,
1134 ReferenceTransformBinding::Computed { scale_from, vertical_flip, rotation } => {
1135 let content_size = &self.iframe_size.last().unwrap();
1136
1137 let mut transform = if let Some(scale_from) = scale_from {
1138 match rotation {
1142 Rotation::Degree0 |
1143 Rotation::Degree180 => {
1144 LayoutTransform::scale(
1145 content_size.width / scale_from.width,
1146 content_size.height / scale_from.height,
1147 1.0
1148 )
1149 },
1150 Rotation::Degree90 |
1151 Rotation::Degree270 => {
1152 LayoutTransform::scale(
1153 content_size.height / scale_from.width,
1154 content_size.width / scale_from.height,
1155 1.0
1156 )
1157
1158 }
1159 }
1160 } else {
1161 LayoutTransform::identity()
1162 };
1163
1164 if vertical_flip {
1165 let content_size = &self.iframe_size.last().unwrap();
1166 let content_height = match rotation {
1167 Rotation::Degree0 | Rotation::Degree180 => content_size.height,
1168 Rotation::Degree90 | Rotation::Degree270 => content_size.width,
1169 };
1170 transform = transform
1171 .then_translate(LayoutVector3D::new(0.0, content_height, 0.0))
1172 .pre_scale(1.0, -1.0, 1.0);
1173 }
1174
1175 let rotate = rotation.to_matrix(**content_size);
1176 let transform = transform.then(&rotate);
1177
1178 PropertyBinding::Value(transform)
1179 },
1180 };
1181
1182 let snap_origin = match info.reference_frame.kind {
1183 ReferenceFrameKind::Transform { should_snap, .. } => should_snap,
1184 ReferenceFrameKind::Perspective { .. } => false,
1185 };
1186
1187 let origin = if snap_origin {
1188 self.snap_point_to_device(info.origin, parent_space)
1193 } else {
1194 info.origin
1195 };
1196
1197 let external_scroll_offset = self.current_external_scroll_offset(parent_space);
1198
1199 self.push_reference_frame(
1200 info.reference_frame.id,
1201 parent_space,
1202 pipeline_id,
1203 info.reference_frame.transform_style,
1204 transform,
1205 info.reference_frame.kind,
1206 (origin + external_scroll_offset).to_vector(),
1207 false,
1208 );
1209 }
1210
1211 fn build_scroll_frame(
1212 &mut self,
1213 info: &ScrollFrameDescriptor,
1214 parent_node_index: SpatialNodeIndex,
1215 pipeline_id: PipelineId,
1216 ) {
1217 let content_size = info.content_rect.size();
1221 let external_scroll_offset = self.current_external_scroll_offset(parent_node_index);
1222
1223 self.add_scroll_frame(
1224 info.scroll_frame_id,
1225 parent_node_index,
1226 info.external_id,
1227 pipeline_id,
1228 &info.frame_rect.translate(external_scroll_offset),
1229 &content_size,
1230 ScrollFrameKind::Explicit,
1231 info.external_scroll_offset,
1232 info.scroll_offset_generation,
1233 info.has_scroll_linked_effect,
1234 );
1235 }
1236
1237 fn push_iframe(
1238 &mut self,
1239 info: &IframeDisplayItem,
1240 spatial_node_index: SpatialNodeIndex,
1241 ) -> Option<BuiltDisplayListIter<'a>> {
1242 let iframe_pipeline_id = info.pipeline_id;
1243 let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
1244 Some(pipeline) => pipeline,
1245 None => {
1246 debug_assert!(info.ignore_missing_pipeline);
1247 return None
1248 },
1249 };
1250
1251 self.clip_tree_builder.push_clip_chain(Some(info.space_and_clip.clip_chain_id), false, false);
1252
1253 self.add_rect_clip_node(
1255 ClipId::root(iframe_pipeline_id),
1256 info.space_and_clip.spatial_id,
1257 &info.clip_rect,
1258 );
1259
1260 self.clip_tree_builder.push_clip_id(ClipId::root(iframe_pipeline_id));
1261
1262 self.id_to_index_mapper_stack.push(NodeIdToIndexMapper::default());
1263
1264 let bounds = self.normalize_rect_scroll_offset(
1265 &info.bounds,
1266 spatial_node_index,
1267 );
1268
1269 let snapped_origin = self.snap_point_to_device(bounds.min, spatial_node_index);
1282 let iframe_size = bounds.size();
1283
1284 let spatial_node_index = self.push_reference_frame(
1285 SpatialId::root_reference_frame(iframe_pipeline_id),
1286 spatial_node_index,
1287 iframe_pipeline_id,
1288 TransformStyle::Flat,
1289 PropertyBinding::Value(LayoutTransform::identity()),
1290 ReferenceFrameKind::Transform {
1291 is_2d_scale_translation: true,
1292 should_snap: true,
1293 paired_with_perspective: false,
1294 },
1295 snapped_origin.to_vector(),
1296 true,
1297 );
1298
1299 let iframe_rect = LayoutRect::from_size(iframe_size);
1300 let is_root_pipeline = self.iframe_size.is_empty();
1301
1302 self.add_scroll_frame(
1303 SpatialId::root_scroll_node(iframe_pipeline_id),
1304 spatial_node_index,
1305 ExternalScrollId(0, iframe_pipeline_id),
1306 iframe_pipeline_id,
1307 &iframe_rect,
1308 &iframe_size,
1309 ScrollFrameKind::PipelineRoot {
1310 is_root_pipeline,
1311 },
1312 LayoutVector2D::zero(),
1313 APZScrollGeneration::default(),
1314 HasScrollLinkedEffect::No,
1315 );
1316
1317 if self.iframe_size.is_empty() {
1320 assert!(self.root_iframe_clip.is_none());
1321 self.root_iframe_clip = Some(ClipId::root(iframe_pipeline_id));
1322 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1323 }
1324 self.iframe_size.push(iframe_size);
1325
1326 self.build_spatial_tree_for_display_list(
1327 &pipeline.display_list,
1328 iframe_pipeline_id,
1329 );
1330
1331 Some(pipeline.display_list.iter())
1332 }
1333
1334 fn get_space(
1335 &self,
1336 spatial_id: SpatialId,
1337 ) -> SpatialNodeIndex {
1338 self.id_to_index_mapper_stack.last().unwrap().get_spatial_node_index(spatial_id)
1339 }
1340
1341 fn get_clip_node(
1342 &mut self,
1343 clip_chain_id: api::ClipChainId,
1344 ) -> ClipNodeId {
1345 self.clip_tree_builder.build_clip_set(
1346 clip_chain_id,
1347 )
1348 }
1349
1350 fn process_common_properties(
1351 &mut self,
1352 common: &CommonItemProperties,
1353 bounds: Option<LayoutRect>,
1354 ) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipNodeId) {
1355 let spatial_node_index = self.get_space(common.spatial_id);
1356
1357 let mut clip_rect = common.clip_rect;
1359 let mut prim_rect = bounds.unwrap_or(clip_rect);
1360 prim_rect = self.normalize_rect_scroll_offset(&prim_rect, spatial_node_index);
1365 clip_rect = self.normalize_rect_scroll_offset(&clip_rect, spatial_node_index);
1366 let unsnapped_rect = prim_rect;
1367
1368
1369 let clip_node_id = self.get_clip_node(
1370 common.clip_chain_id,
1371 );
1372
1373 let layout = LayoutPrimitiveInfo {
1374 rect: prim_rect,
1375 clip_rect,
1376 flags: common.flags,
1377 aligned_aa_edges: EdgeMask::empty(),
1382 transformed_aa_edges: EdgeMask::all(),
1383 };
1384
1385 (layout, unsnapped_rect, spatial_node_index, clip_node_id)
1386 }
1387
1388 fn process_common_properties_with_bounds(
1389 &mut self,
1390 common: &CommonItemProperties,
1391 bounds: LayoutRect,
1392 ) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipNodeId) {
1393 self.process_common_properties(
1394 common,
1395 Some(bounds),
1396 )
1397 }
1398
1399 fn normalize_rect_scroll_offset(
1403 &mut self,
1404 rect: &LayoutRect,
1405 spatial_node_index: SpatialNodeIndex,
1406 ) -> LayoutRect {
1407 let current_offset = self.current_external_scroll_offset(spatial_node_index);
1408
1409 rect.translate(current_offset)
1410 }
1411
1412 fn snap_point_to_device(
1422 &self,
1423 point: LayoutPoint,
1424 target_spatial_node: SpatialNodeIndex,
1425 ) -> LayoutPoint {
1426 let root = self.spatial_tree.root_reference_frame_index();
1427 let mut snapper = SpaceSnapper::new(root, RasterPixelScale::new(1.0));
1428 snapper.set_target_spatial_node(target_spatial_node, self.spatial_tree);
1429 snapper.snap_point(&point)
1430 }
1431
1432
1433 fn build_item<'b>(
1434 &'b mut self,
1435 item: DisplayItemRef,
1436 ) {
1437 match *item.item() {
1438 DisplayItem::Image(ref info) => {
1439 profile_scope!("image");
1440
1441 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1442 &info.common,
1443 info.bounds,
1444 );
1445
1446 self.add_image(
1447 spatial_node_index,
1448 clip_node_id,
1449 &layout,
1450 StretchSizeKey::fills_prim(),
1451 LayoutSize::zero(),
1452 info.image_key,
1453 info.image_rendering,
1454 info.alpha_type,
1455 info.color,
1456 );
1457 }
1458 DisplayItem::RepeatingImage(ref info) => {
1459 profile_scope!("repeating_image");
1460
1461 let (layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1462 &info.common,
1463 info.bounds,
1464 );
1465
1466 let stretch_size = process_image_stretch_size(
1467 &unsnapped_rect,
1468 info.stretch_size,
1469 );
1470
1471 self.add_image(
1472 spatial_node_index,
1473 clip_node_id,
1474 &layout,
1475 stretch_size,
1476 info.tile_spacing,
1477 info.image_key,
1478 info.image_rendering,
1479 info.alpha_type,
1480 info.color,
1481 );
1482 }
1483 DisplayItem::YuvImage(ref info) => {
1484 profile_scope!("yuv_image");
1485
1486 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1487 &info.common,
1488 info.bounds,
1489 );
1490
1491 self.add_yuv_image(
1492 spatial_node_index,
1493 clip_node_id,
1494 &layout,
1495 info.yuv_data,
1496 info.color_depth,
1497 info.color_space,
1498 info.color_range,
1499 info.image_rendering,
1500 );
1501 }
1502 DisplayItem::Text(ref info) => {
1503 profile_scope!("text");
1504
1505 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1512 &info.common,
1513 info.bounds,
1514 );
1515
1516 self.add_text(
1517 spatial_node_index,
1518 clip_node_id,
1519 &layout,
1520 &info.font_key,
1521 &info.color,
1522 item.glyphs(),
1523 info.glyph_options,
1524 );
1525 }
1526 DisplayItem::Rectangle(ref info) => {
1527 profile_scope!("rect");
1528
1529 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1530 &info.common,
1531 info.bounds,
1532 );
1533
1534 self.add_primitive(
1535 spatial_node_index,
1536 clip_node_id,
1537 &layout,
1538 Vec::new(),
1539 RectanglePrim {
1540 color: info.color.into(),
1541 },
1542 );
1543
1544 if info.common.flags.contains(PrimitiveFlags::CHECKERBOARD_BACKGROUND) {
1545 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1546 }
1547 }
1548 DisplayItem::HitTest(ref info) => {
1549 profile_scope!("hit_test");
1550
1551 let spatial_node_index = self.get_space(info.spatial_id);
1552
1553 let rect = self.normalize_rect_scroll_offset(
1554 &info.rect,
1555 spatial_node_index,
1556 );
1557
1558 let layout = LayoutPrimitiveInfo {
1559 rect,
1560 clip_rect: rect,
1561 flags: info.flags,
1562 aligned_aa_edges: EdgeMask::empty(),
1563 transformed_aa_edges: EdgeMask::empty(),
1564 };
1565
1566 let spatial_node = self.spatial_tree.get_node_info(spatial_node_index);
1567 let anim_id: u64 = match spatial_node.node_type {
1568 SpatialNodeType::ReferenceFrame(ReferenceFrameInfo {
1569 source_transform: PropertyBinding::Binding(key, _),
1570 ..
1571 }) => key.clone().into(),
1572 SpatialNodeType::StickyFrame(StickyFrameInfo {
1573 transform: Some(PropertyBinding::Binding(key, _)),
1574 ..
1575 }) => key.clone().into(),
1576 _ => 0,
1577 };
1578
1579 let clip_node_id = self.get_clip_node(info.clip_chain_id);
1580
1581 self.add_primitive_to_hit_testing_list(
1582 &layout,
1583 spatial_node_index,
1584 clip_node_id,
1585 info.tag,
1586 anim_id,
1587 );
1588 }
1589 DisplayItem::Line(ref info) => {
1590 profile_scope!("line");
1591
1592 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1593 &info.common,
1594 info.area,
1595 );
1596
1597 self.add_line(
1598 spatial_node_index,
1599 clip_node_id,
1600 &layout,
1601 info.wavy_line_thickness,
1602 info.orientation,
1603 info.color,
1604 info.style,
1605 );
1606 }
1607 DisplayItem::Gradient(ref info) => {
1608 profile_scope!("gradient");
1609
1610 if !info.gradient.is_valid() {
1611 return;
1612 }
1613
1614 let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1615 &info.common,
1616 info.bounds,
1617 );
1618
1619 let mut tile_size = process_repeat_size(
1620 &layout.rect,
1621 &unsnapped_rect,
1622 info.tile_size,
1623 );
1624
1625 let stops = read_gradient_stops(item.gradient_stops());
1626 let mut start = info.gradient.start_point;
1627 let mut end = info.gradient.end_point;
1628 optimize_linear_gradient(
1633 &mut layout.rect,
1634 &mut tile_size,
1635 info.tile_spacing,
1636 &layout.clip_rect,
1637 &mut start,
1638 &mut end,
1639 );
1640
1641 if !tile_size.ceil().is_empty() {
1642 if let Some(prim_key_kind) = self.create_linear_gradient_prim(
1643 &layout,
1644 start,
1645 end,
1646 stops,
1647 info.gradient.extend_mode,
1648 tile_size,
1649 info.tile_spacing,
1650 None,
1651 EdgeMask::all(),
1652 ) {
1653 self.add_nonshadowable_primitive(
1654 spatial_node_index,
1655 clip_node_id,
1656 &layout,
1657 Vec::new(),
1658 prim_key_kind,
1659 );
1660 }
1661 }
1662 }
1663 DisplayItem::RadialGradient(ref info) => {
1664 profile_scope!("radial");
1665
1666 if !info.gradient.is_valid() {
1667 return;
1668 }
1669
1670 let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1671 &info.common,
1672 info.bounds,
1673 );
1674
1675 let mut center = info.gradient.center;
1676
1677 let stops = read_gradient_stops(item.gradient_stops());
1678
1679 let mut tile_size = process_repeat_size(
1680 &layout.rect,
1681 &unsnapped_rect,
1682 info.tile_size,
1683 );
1684
1685 let mut prim_rect = layout.rect;
1686 let mut tile_spacing = info.tile_spacing;
1687 let mut aa_mask = EdgeMask::all();
1688 optimize_radial_gradient(
1689 &mut prim_rect,
1690 &mut tile_size,
1691 &mut center,
1692 &mut tile_spacing,
1693 &mut aa_mask,
1694 &layout.clip_rect,
1695 info.gradient.radius,
1696 info.gradient.end_offset,
1697 info.gradient.extend_mode,
1698 &stops,
1699 &mut |solid_rect, color, aa_mask| {
1700 self.add_nonshadowable_primitive(
1701 spatial_node_index,
1702 clip_node_id,
1703 &LayoutPrimitiveInfo {
1704 rect: *solid_rect,
1705 aligned_aa_edges: layout.aligned_aa_edges & aa_mask,
1706 transformed_aa_edges: layout.transformed_aa_edges & aa_mask,
1707 .. layout
1708 },
1709 Vec::new(),
1710 RectanglePrim { color: PropertyBinding::Value(color) },
1711 );
1712 }
1713 );
1714
1715 layout.aligned_aa_edges &= aa_mask;
1716 layout.transformed_aa_edges &= aa_mask;
1717
1718 simplify_repeated_primitive(&tile_size, &mut tile_spacing, &mut prim_rect);
1723
1724 if !tile_size.ceil().is_empty() {
1725 layout.rect = prim_rect;
1726 let prim_key_kind = self.create_radial_gradient_prim(
1727 &layout,
1728 center,
1729 info.gradient.start_offset * info.gradient.radius.width,
1730 info.gradient.end_offset * info.gradient.radius.width,
1731 info.gradient.radius.width / info.gradient.radius.height,
1732 stops,
1733 info.gradient.extend_mode,
1734 tile_size,
1735 tile_spacing,
1736 None,
1737 );
1738
1739 self.add_nonshadowable_primitive(
1740 spatial_node_index,
1741 clip_node_id,
1742 &layout,
1743 Vec::new(),
1744 prim_key_kind,
1745 );
1746 }
1747 }
1748 DisplayItem::ConicGradient(ref info) => {
1749 profile_scope!("conic");
1750
1751 if !info.gradient.is_valid() {
1752 return;
1753 }
1754
1755 let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1756 &info.common,
1757 info.bounds,
1758 );
1759
1760 let tile_size = process_repeat_size(
1761 &layout.rect,
1762 &unsnapped_rect,
1763 info.tile_size,
1764 );
1765
1766 let offset = apply_gradient_local_clip(
1767 &mut layout.rect,
1768 &tile_size,
1769 &info.tile_spacing,
1770 &layout.clip_rect,
1771 );
1772 let center = info.gradient.center + offset;
1773
1774 if !tile_size.ceil().is_empty() {
1775 let prim_key_kind = self.create_conic_gradient_prim(
1776 &layout,
1777 center,
1778 info.gradient.angle,
1779 info.gradient.start_offset,
1780 info.gradient.end_offset,
1781 item.gradient_stops(),
1782 info.gradient.extend_mode,
1783 tile_size,
1784 info.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::BoxShadow(ref info) => {
1798 profile_scope!("box_shadow");
1799
1800 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1801 &info.common,
1802 info.box_bounds,
1803 );
1804
1805 self.add_box_shadow(
1806 spatial_node_index,
1807 clip_node_id,
1808 &layout,
1809 &info.offset,
1810 info.color,
1811 info.blur_radius,
1812 info.spread_radius,
1813 info.border_radius,
1814 info.shadow_radius,
1815 info.clip_mode,
1816 );
1817 }
1818 DisplayItem::Border(ref info) => {
1819 profile_scope!("border");
1820
1821 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1822 &info.common,
1823 info.bounds,
1824 );
1825
1826 self.add_border(
1827 spatial_node_index,
1828 clip_node_id,
1829 &layout,
1830 info,
1831 item.gradient_stops(),
1832 );
1833 }
1834 DisplayItem::ImageMaskClip(ref info) => {
1835 profile_scope!("image_clip");
1836
1837 self.add_image_mask_clip_node(
1838 info.id,
1839 info.spatial_id,
1840 &info.image_mask,
1841 info.fill_rule,
1842 item.points(),
1843 );
1844 }
1845 DisplayItem::RoundedRectClip(ref info) => {
1846 profile_scope!("rounded_clip");
1847
1848 self.add_rounded_rect_clip_node(
1849 info.id,
1850 info.spatial_id,
1851 &info.clip,
1852 );
1853 }
1854 DisplayItem::RectClip(ref info) => {
1855 profile_scope!("rect_clip");
1856
1857 self.add_rect_clip_node(
1858 info.id,
1859 info.spatial_id,
1860 &info.clip_rect,
1861 );
1862 }
1863 DisplayItem::ClipChain(ref info) => {
1864 profile_scope!("clip_chain");
1865
1866 self.clip_tree_builder.define_clip_chain(
1867 info.id,
1868 info.parent,
1869 item.clip_chain_items().into_iter(),
1870 );
1871 },
1872 DisplayItem::BackdropFilter(ref info) => {
1873 profile_scope!("backdrop");
1874
1875 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties(
1876 &info.common,
1877 None,
1878 );
1879
1880 let filters = filter_ops_for_compositing(item.filters());
1881 let filter_datas = filter_datas_for_compositing(item.filter_datas());
1882
1883 self.add_backdrop_filter(
1884 spatial_node_index,
1885 clip_node_id,
1886 &layout,
1887 filters,
1888 filter_datas,
1889 );
1890 }
1891
1892 DisplayItem::SetGradientStops |
1894 DisplayItem::SetFilterOps |
1895 DisplayItem::SetFilterData |
1896 DisplayItem::SetPoints => {}
1897
1898 DisplayItem::PushStackingContext(..) |
1900 DisplayItem::PushReferenceFrame(..) |
1901 DisplayItem::PopReferenceFrame |
1902 DisplayItem::PopStackingContext |
1903 DisplayItem::Iframe(_) => {
1904 unreachable!("Handled in `build_all`")
1905 }
1906
1907 DisplayItem::PushShadow(info) => {
1908 profile_scope!("push_shadow");
1909
1910 let spatial_node_index = self.get_space(info.space_and_clip.spatial_id);
1911
1912 self.push_shadow(
1913 info.shadow,
1914 spatial_node_index,
1915 info.space_and_clip.clip_chain_id,
1916 info.should_inflate,
1917 );
1918 }
1919 DisplayItem::PopAllShadows => {
1920 profile_scope!("pop_all_shadows");
1921
1922 self.pop_all_shadows();
1923 }
1924 DisplayItem::DebugMarker(..) => {}
1925 }
1926 }
1927
1928 fn create_primitive<P>(
1934 &mut self,
1935 info: &LayoutPrimitiveInfo,
1936 clip_leaf_id: ClipLeafId,
1937 prim: P,
1938 ) -> PrimitiveInstance
1939 where
1940 P: InternablePrimitive,
1941 Interners: AsMut<Interner<P>>,
1942 {
1943 let prim_key = prim.into_key(info);
1945
1946 let interner = self.interners.as_mut();
1947 let prim_data_handle = interner
1948 .intern(&prim_key, || ());
1949
1950 let instance_kind = P::make_instance_kind(
1951 prim_key,
1952 prim_data_handle,
1953 &mut self.prim_store,
1954 );
1955
1956 PrimitiveInstance::new(
1957 instance_kind,
1958 clip_leaf_id,
1959 info.rect,
1960 )
1961 }
1962
1963 fn add_primitive_to_hit_testing_list(
1964 &mut self,
1965 info: &LayoutPrimitiveInfo,
1966 spatial_node_index: SpatialNodeIndex,
1967 clip_node_id: ClipNodeId,
1968 tag: ItemTag,
1969 anim_id: u64,
1970 ) {
1971 self.hit_testing_scene.add_item(
1972 tag,
1973 anim_id,
1974 info,
1975 spatial_node_index,
1976 clip_node_id,
1977 &self.clip_tree_builder,
1978 self.interners,
1979 );
1980 }
1981
1982 pub fn add_primitive_to_draw_list(
1984 &mut self,
1985 prim_instance: PrimitiveInstance,
1986 prim_rect: LayoutRect,
1987 spatial_node_index: SpatialNodeIndex,
1988 flags: PrimitiveFlags,
1989 ) {
1990 match self.sc_stack.last_mut() {
1996 Some(stacking_context) => {
1997 stacking_context.prim_list.add_prim(
1998 prim_instance,
1999 prim_rect,
2000 spatial_node_index,
2001 flags,
2002 &mut self.prim_instances,
2003 &self.clip_tree_builder,
2004 );
2005 }
2006 None => {
2007 self.tile_cache_builder.add_prim(
2008 prim_instance,
2009 prim_rect,
2010 spatial_node_index,
2011 flags,
2012 self.spatial_tree,
2013 &self.quality_settings,
2014 &mut self.prim_instances,
2015 &self.clip_tree_builder,
2016 );
2017 }
2018 }
2019 }
2020
2021 pub fn add_nonshadowable_primitive<P>(
2024 &mut self,
2025 spatial_node_index: SpatialNodeIndex,
2026 clip_node_id: ClipNodeId,
2027 info: &LayoutPrimitiveInfo,
2028 clip_items: Vec<ClipItemEntry>,
2029 prim: P,
2030 )
2031 where
2032 P: InternablePrimitive + IsVisible,
2033 Interners: AsMut<Interner<P>>,
2034 {
2035 if prim.is_visible() {
2036 let clip_leaf_id = self.clip_tree_builder.build_for_prim(
2037 clip_node_id,
2038 info,
2039 &clip_items,
2040 &mut self.interners,
2041 );
2042
2043 self.add_prim_to_draw_list(
2044 info,
2045 spatial_node_index,
2046 clip_leaf_id,
2047 prim,
2048 );
2049 }
2050 }
2051
2052 pub fn add_primitive<P>(
2053 &mut self,
2054 spatial_node_index: SpatialNodeIndex,
2055 clip_node_id: ClipNodeId,
2056 info: &LayoutPrimitiveInfo,
2057 clip_items: Vec<ClipItemEntry>,
2058 prim: P,
2059 )
2060 where
2061 P: InternablePrimitive + IsVisible,
2062 Interners: AsMut<Interner<P>>,
2063 ShadowItem: From<PendingPrimitive<P>>
2064 {
2065 if self.pending_shadow_items.is_empty() {
2068 self.add_nonshadowable_primitive(
2069 spatial_node_index,
2070 clip_node_id,
2071 info,
2072 clip_items,
2073 prim,
2074 );
2075 } else {
2076 debug_assert!(clip_items.is_empty(), "No per-prim clips expected for shadowed primitives");
2077
2078 self.pending_shadow_items.push_back(PendingPrimitive {
2081 spatial_node_index,
2082 clip_node_id,
2083 info: *info,
2084 prim,
2085 }.into());
2086 }
2087 }
2088
2089 fn add_prim_to_draw_list<P>(
2090 &mut self,
2091 info: &LayoutPrimitiveInfo,
2092 spatial_node_index: SpatialNodeIndex,
2093 clip_leaf_id: ClipLeafId,
2094 prim: P,
2095 )
2096 where
2097 P: InternablePrimitive,
2098 Interners: AsMut<Interner<P>>,
2099 {
2100 let prim_instance = self.create_primitive(
2101 info,
2102 clip_leaf_id,
2103 prim,
2104 );
2105 self.add_primitive_to_draw_list(
2106 prim_instance,
2107 info.rect,
2108 spatial_node_index,
2109 info.flags,
2110 );
2111 }
2112
2113 fn make_current_slice_atomic_if_required(&mut self) {
2114 let has_non_wrapping_sc = self.sc_stack
2115 .iter()
2116 .position(|sc| {
2117 !sc.flags.contains(StackingContextFlags::WRAPS_BACKDROP_FILTER)
2118 })
2119 .is_some();
2120
2121 if has_non_wrapping_sc {
2122 return;
2123 }
2124
2125 assert!(self.pending_shadow_items.is_empty());
2127 self.tile_cache_builder.make_current_slice_atomic();
2128 }
2129
2130 fn add_tile_cache_barrier_if_needed(
2133 &mut self,
2134 slice_flags: SliceFlags,
2135 ) {
2136 if self.sc_stack.is_empty() {
2137 assert!(self.pending_shadow_items.is_empty());
2139
2140 self.tile_cache_builder.add_tile_cache_barrier(
2141 slice_flags,
2142 self.root_iframe_clip,
2143 );
2144 }
2145 }
2146
2147 fn push_stacking_context(
2149 &mut self,
2150 mut composite_ops: CompositeOps,
2151 transform_style: TransformStyle,
2152 prim_flags: PrimitiveFlags,
2153 spatial_node_index: SpatialNodeIndex,
2154 clip_chain_id: Option<api::ClipChainId>,
2155 requested_raster_space: RasterSpace,
2156 flags: StackingContextFlags,
2157 ) -> StackingContextInfo {
2158 profile_scope!("push_stacking_context");
2159
2160 let needs_extra_stacking_context = composite_ops.snapshot.is_some()
2169 && composite_ops.has_valid_filters();
2170
2171 if needs_extra_stacking_context {
2172 let snapshot = mem::take(&mut composite_ops.snapshot);
2173 let mut info = self.push_stacking_context(
2174 CompositeOps {
2175 filters: Vec::new(),
2176 filter_datas: Vec::new(),
2177 mix_blend_mode: None,
2178 snapshot,
2179 },
2180 TransformStyle::Flat,
2181 prim_flags,
2182 spatial_node_index,
2183 clip_chain_id,
2184 requested_raster_space,
2185 flags,
2186 );
2187 info.pop_stacking_context = true;
2188 self.extra_stacking_context_stack.push(info);
2189 }
2190
2191 let clip_node_id = match clip_chain_id {
2192 Some(id) => {
2193 self.clip_tree_builder.build_clip_set(id)
2194 }
2195 None => {
2196 self.clip_tree_builder.build_clip_set(api::ClipChainId::INVALID)
2197 }
2198 };
2199
2200 self.clip_tree_builder.push_clip_chain(
2201 clip_chain_id,
2202 !composite_ops.is_empty(),
2203 composite_ops.snapshot.is_some(),
2204 );
2205
2206 let new_space = match (self.raster_space_stack.last(), requested_raster_space) {
2207 (None, _) => requested_raster_space,
2209 (Some(parent_space), RasterSpace::Screen) => *parent_space,
2211 (Some(RasterSpace::Screen), space) => space,
2213 (Some(RasterSpace::Local(parent_scale)), RasterSpace::Local(scale)) => RasterSpace::Local(parent_scale.max(scale)),
2215 };
2216 self.raster_space_stack.push(new_space);
2217
2218 let (parent_is_3d, extra_3d_instance, plane_splitter_index) = match self.sc_stack.last_mut() {
2222 Some(ref mut sc) if sc.is_3d() => {
2223 let (flat_items_context_3d, plane_splitter_index) = match sc.context_3d {
2224 Picture3DContext::In { ancestor_index, plane_splitter_index, .. } => {
2225 (
2226 Picture3DContext::In {
2227 root_data: None,
2228 ancestor_index,
2229 plane_splitter_index,
2230 },
2231 plane_splitter_index,
2232 )
2233 }
2234 Picture3DContext::Out => panic!("Unexpected out of 3D context"),
2235 };
2236 let extra_instance = sc.cut_item_sequence(
2239 &mut self.prim_store,
2240 &mut self.interners,
2241 Some(PictureCompositeMode::Blit(BlitReason::PRESERVE3D)),
2242 flat_items_context_3d,
2243 &mut self.clip_tree_builder,
2244 );
2245 let extra_instance = extra_instance.map(|(_, instance)| {
2246 ExtendedPrimitiveInstance {
2247 instance,
2248 spatial_node_index: sc.spatial_node_index,
2249 flags: sc.prim_flags,
2250 }
2251 });
2252 (true, extra_instance, Some(plane_splitter_index))
2253 },
2254 _ => (false, None, None),
2255 };
2256
2257 if let Some(instance) = extra_3d_instance {
2258 self.add_primitive_instance_to_3d_root(instance);
2259 }
2260
2261 let participating_in_3d_context =
2267 composite_ops.is_empty() &&
2268 (parent_is_3d || transform_style == TransformStyle::Preserve3D);
2269
2270 let context_3d = if participating_in_3d_context {
2271 let ancestor_index = self.containing_block_stack
2274 .last()
2275 .cloned()
2276 .unwrap_or(self.spatial_tree.root_reference_frame_index());
2277
2278 let plane_splitter_index = plane_splitter_index.unwrap_or_else(|| {
2279 let index = self.next_plane_splitter_index;
2280 self.next_plane_splitter_index += 1;
2281 PlaneSplitterIndex(index)
2282 });
2283
2284 Picture3DContext::In {
2285 root_data: if parent_is_3d {
2286 None
2287 } else {
2288 Some(Vec::new())
2289 },
2290 plane_splitter_index,
2291 ancestor_index,
2292 }
2293 } else {
2294 Picture3DContext::Out
2295 };
2296
2297 let mut blit_reason = BlitReason::empty();
2302
2303 if flags.contains(StackingContextFlags::FORCED_ISOLATION) {
2306 blit_reason = BlitReason::FORCED_ISOLATION;
2307 }
2308
2309 if composite_ops.snapshot.is_some() {
2311 blit_reason = BlitReason::SNAPSHOT;
2312 }
2313
2314 if let Some(clip_chain_id) = clip_chain_id {
2317 if self.clip_tree_builder.clip_chain_has_complex_clips(clip_chain_id, &self.interners) {
2318 if !self.sc_stack.is_empty() ||
2326 !self.clip_tree_builder.clip_chain_complex_clips_are_promotable(
2327 clip_chain_id,
2328 &self.interners,
2329 &self.spatial_tree,
2330 )
2331 {
2332 blit_reason |= BlitReason::CLIP;
2333 }
2334 }
2335 }
2336
2337 let mut is_redundant = FlattenedStackingContext::is_redundant(
2340 &context_3d,
2341 &composite_ops,
2342 blit_reason,
2343 self.sc_stack.last(),
2344 prim_flags,
2345 );
2346
2347 if flags.contains(StackingContextFlags::IS_BLEND_CONTAINER) {
2353 if !self.sc_stack.is_empty() {
2355 blit_reason |= BlitReason::BLEND_MODE;
2358 is_redundant = false;
2359 } else {
2360 if self.tile_cache_builder.is_current_slice_empty() &&
2364 self.spatial_tree.is_root_coord_system(spatial_node_index) &&
2365 !self.clip_tree_builder.clip_node_has_complex_clips(clip_node_id, &self.interners)
2366 {
2367 self.add_tile_cache_barrier_if_needed(SliceFlags::IS_ATOMIC);
2368 self.tile_cache_builder.make_current_slice_atomic();
2369 } else {
2370 blit_reason |= BlitReason::BLEND_MODE;
2374 is_redundant = false;
2375 }
2376 }
2377 }
2378
2379 let set_tile_cache_barrier = prim_flags.contains(PrimitiveFlags::IS_SCROLLBAR_CONTAINER);
2382
2383 if set_tile_cache_barrier {
2384 self.add_tile_cache_barrier_if_needed(SliceFlags::IS_SCROLLBAR);
2385 }
2386
2387 let mut sc_info = StackingContextInfo {
2388 pop_stacking_context: false,
2389 pop_containing_block: false,
2390 set_tile_cache_barrier,
2391 needs_extra_stacking_context,
2392 };
2393
2394 if !participating_in_3d_context {
2396 sc_info.pop_containing_block = true;
2397 self.containing_block_stack.push(spatial_node_index);
2398 }
2399
2400 if !is_redundant {
2402 sc_info.pop_stacking_context = true;
2403
2404 self.sc_stack.push(FlattenedStackingContext {
2407 prim_list: PrimitiveList::empty(),
2408 prim_flags,
2409 spatial_node_index,
2410 clip_node_id,
2411 composite_ops,
2412 blit_reason,
2413 transform_style,
2414 context_3d,
2415 flags,
2416 raster_space: new_space,
2417 });
2418 }
2419
2420 sc_info
2421 }
2422
2423 fn pop_stacking_context(
2424 &mut self,
2425 info: StackingContextInfo,
2426 ) {
2427 profile_scope!("pop_stacking_context");
2428
2429 self.clip_tree_builder.pop_clip();
2430
2431 self.raster_space_stack.pop().unwrap();
2433
2434 if info.pop_containing_block {
2436 self.containing_block_stack.pop().unwrap();
2437 }
2438
2439 if info.set_tile_cache_barrier {
2440 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
2441 }
2442
2443 if !info.pop_stacking_context {
2445 return;
2446 }
2447
2448 let stacking_context = self.sc_stack.pop().unwrap();
2449
2450 let mut source = match stacking_context.context_3d {
2451 Picture3DContext::In { ancestor_index, plane_splitter_index, .. } => {
2459 let composite_mode = Some(
2460 PictureCompositeMode::Blit(BlitReason::PRESERVE3D | stacking_context.blit_reason)
2461 );
2462
2463 let pic_index = PictureIndex(self.prim_store.pictures
2465 .alloc()
2466 .init(PictureInstance::new_image(
2467 composite_mode.clone(),
2468 Picture3DContext::In { root_data: None, ancestor_index, plane_splitter_index },
2469 stacking_context.prim_flags,
2470 stacking_context.prim_list,
2471 stacking_context.spatial_node_index,
2472 stacking_context.raster_space,
2473 PictureFlags::empty(),
2474 None,
2475 ))
2476 );
2477
2478 let instance = create_prim_instance(
2479 pic_index,
2480 composite_mode.into(),
2481 stacking_context.raster_space,
2482 stacking_context.clip_node_id,
2483 &mut self.interners,
2484 &mut self.clip_tree_builder,
2485 );
2486
2487 PictureChainBuilder::from_instance(
2488 instance,
2489 stacking_context.prim_flags,
2490 stacking_context.spatial_node_index,
2491 stacking_context.raster_space,
2492 )
2493 }
2494 Picture3DContext::Out => {
2495 if stacking_context.blit_reason.is_empty() {
2496 PictureChainBuilder::from_prim_list(
2497 stacking_context.prim_list,
2498 stacking_context.prim_flags,
2499 stacking_context.spatial_node_index,
2500 stacking_context.raster_space,
2501 false,
2502 )
2503 } else {
2504 let composite_mode = Some(
2505 PictureCompositeMode::Blit(stacking_context.blit_reason)
2506 );
2507
2508 let pic_index = PictureIndex(self.prim_store.pictures
2510 .alloc()
2511 .init(PictureInstance::new_image(
2512 composite_mode.clone(),
2513 Picture3DContext::Out,
2514 stacking_context.prim_flags,
2515 stacking_context.prim_list,
2516 stacking_context.spatial_node_index,
2517 stacking_context.raster_space,
2518 PictureFlags::empty(),
2519 None,
2520 ))
2521 );
2522
2523 let instance = create_prim_instance(
2524 pic_index,
2525 composite_mode.into(),
2526 stacking_context.raster_space,
2527 stacking_context.clip_node_id,
2528 &mut self.interners,
2529 &mut self.clip_tree_builder,
2530 );
2531
2532 PictureChainBuilder::from_instance(
2533 instance,
2534 stacking_context.prim_flags,
2535 stacking_context.spatial_node_index,
2536 stacking_context.raster_space,
2537 )
2538 }
2539 }
2540 };
2541
2542 if let Picture3DContext::In { root_data: Some(mut prims), ancestor_index, plane_splitter_index } = stacking_context.context_3d {
2546 let instance = source.finalize(
2547 ClipNodeId::NONE,
2548 &mut self.interners,
2549 &mut self.prim_store,
2550 &mut self.clip_tree_builder,
2551 None,
2552 );
2553
2554 prims.push(ExtendedPrimitiveInstance {
2555 instance,
2556 spatial_node_index: stacking_context.spatial_node_index,
2557 flags: stacking_context.prim_flags,
2558 });
2559
2560 let mut prim_list = PrimitiveList::empty();
2561
2562 let mut needs_3d_context = false;
2575
2576 for ext_prim in prims.drain(..) {
2577 if !self.spatial_tree.is_root_coord_system(ext_prim.spatial_node_index) {
2582 needs_3d_context = true;
2583 }
2584
2585 prim_list.add_prim(
2586 ext_prim.instance,
2587 LayoutRect::zero(),
2588 ext_prim.spatial_node_index,
2589 ext_prim.flags,
2590 &mut self.prim_instances,
2591 &self.clip_tree_builder,
2592 );
2593 }
2594
2595 let context_3d = if needs_3d_context {
2596 Picture3DContext::In {
2597 root_data: Some(Vec::new()),
2598 ancestor_index,
2599 plane_splitter_index,
2600 }
2601 } else {
2602 for child_pic_index in &prim_list.child_pictures {
2606 let child_pic = &mut self.prim_store.pictures[child_pic_index.0];
2607 let needs_surface = child_pic.snapshot.is_some();
2608 if !needs_surface {
2609 child_pic.composite_mode = None;
2610 }
2611 child_pic.context_3d = Picture3DContext::Out;
2612 }
2613
2614 Picture3DContext::Out
2615 };
2616
2617 let pic_index = PictureIndex(self.prim_store.pictures
2619 .alloc()
2620 .init(PictureInstance::new_image(
2621 None,
2622 context_3d,
2623 stacking_context.prim_flags,
2624 prim_list,
2625 stacking_context.spatial_node_index,
2626 stacking_context.raster_space,
2627 PictureFlags::empty(),
2628 None,
2629 ))
2630 );
2631
2632 let instance = create_prim_instance(
2633 pic_index,
2634 PictureCompositeKey::Identity,
2635 stacking_context.raster_space,
2636 stacking_context.clip_node_id,
2637 &mut self.interners,
2638 &mut self.clip_tree_builder,
2639 );
2640
2641 source = PictureChainBuilder::from_instance(
2642 instance,
2643 stacking_context.prim_flags,
2644 stacking_context.spatial_node_index,
2645 stacking_context.raster_space,
2646 );
2647 }
2648
2649 let has_filters = stacking_context.composite_ops.has_valid_filters();
2650
2651 let spatial_node_context_offset =
2652 self.current_external_scroll_offset(stacking_context.spatial_node_index);
2653 source = self.wrap_prim_with_filters(
2654 source,
2655 stacking_context.clip_node_id,
2656 stacking_context.composite_ops.filters,
2657 stacking_context.composite_ops.filter_datas,
2658 false,
2659 spatial_node_context_offset,
2660 );
2661
2662 if let Some(mix_blend_mode) = stacking_context.composite_ops.mix_blend_mode {
2675 let composite_mode = PictureCompositeMode::MixBlend(mix_blend_mode);
2676
2677 source = source.add_picture(
2678 composite_mode,
2679 stacking_context.clip_node_id,
2680 Picture3DContext::Out,
2681 &mut self.interners,
2682 &mut self.prim_store,
2683 &mut self.prim_instances,
2684 &mut self.clip_tree_builder,
2685 );
2686 }
2687
2688 let cur_instance = source.finalize(
2691 stacking_context.clip_node_id,
2692 &mut self.interners,
2693 &mut self.prim_store,
2694 &mut self.clip_tree_builder,
2695 stacking_context.composite_ops.snapshot,
2696 );
2697
2698 if stacking_context.composite_ops.snapshot.is_some() {
2699 let pic_index = cur_instance.kind.as_pic();
2700 self.snapshot_pictures.push(pic_index);
2701 }
2702
2703 let trailing_children_instance = match self.sc_stack.last_mut() {
2706 Some(ref parent_sc) if !has_filters && parent_sc.is_3d() => {
2708 Some(cur_instance)
2709 }
2710 Some(ref mut parent_sc) => {
2712 parent_sc.prim_list.add_prim(
2713 cur_instance,
2714 LayoutRect::zero(),
2715 stacking_context.spatial_node_index,
2716 stacking_context.prim_flags,
2717 &mut self.prim_instances,
2718 &self.clip_tree_builder,
2719 );
2720 None
2721 }
2722 None => {
2724 self.add_primitive_to_draw_list(
2725 cur_instance,
2726 LayoutRect::zero(),
2727 stacking_context.spatial_node_index,
2728 stacking_context.prim_flags,
2729 );
2730
2731 None
2732 }
2733 };
2734
2735 if let Some(instance) = trailing_children_instance {
2738 self.add_primitive_instance_to_3d_root(ExtendedPrimitiveInstance {
2739 instance,
2740 spatial_node_index: stacking_context.spatial_node_index,
2741 flags: stacking_context.prim_flags,
2742 });
2743 }
2744
2745 assert!(
2746 self.pending_shadow_items.is_empty(),
2747 "Found unpopped shadows when popping stacking context!"
2748 );
2749
2750 if info.needs_extra_stacking_context {
2751 let inner_info = self.extra_stacking_context_stack.pop().unwrap();
2752 self.pop_stacking_context(inner_info);
2753 }
2754 }
2755
2756 pub fn push_reference_frame(
2757 &mut self,
2758 reference_frame_id: SpatialId,
2759 parent_index: SpatialNodeIndex,
2760 pipeline_id: PipelineId,
2761 transform_style: TransformStyle,
2762 source_transform: PropertyBinding<LayoutTransform>,
2763 kind: ReferenceFrameKind,
2764 origin_in_parent_reference_frame: LayoutVector2D,
2765 is_pipeline_root: bool,
2766 ) -> SpatialNodeIndex {
2767 let index = self.spatial_tree.add_reference_frame(
2768 parent_index,
2769 transform_style,
2770 source_transform,
2771 kind,
2772 origin_in_parent_reference_frame,
2773 pipeline_id,
2774 is_pipeline_root,
2775 );
2776 self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(reference_frame_id, index);
2777
2778 index
2779 }
2780
2781 fn push_root(
2782 &mut self,
2783 pipeline_id: PipelineId,
2784 ) {
2785 let spatial_node_index = self.push_reference_frame(
2786 SpatialId::root_reference_frame(pipeline_id),
2787 self.spatial_tree.root_reference_frame_index(),
2788 pipeline_id,
2789 TransformStyle::Flat,
2790 PropertyBinding::Value(LayoutTransform::identity()),
2791 ReferenceFrameKind::Transform {
2792 is_2d_scale_translation: true,
2793 should_snap: true,
2794 paired_with_perspective: false,
2795 },
2796 LayoutVector2D::zero(),
2797 true,
2798 );
2799
2800 let viewport_rect = LayoutRect::max_rect();
2801
2802 self.add_scroll_frame(
2803 SpatialId::root_scroll_node(pipeline_id),
2804 spatial_node_index,
2805 ExternalScrollId(0, pipeline_id),
2806 pipeline_id,
2807 &viewport_rect,
2808 &viewport_rect.size(),
2809 ScrollFrameKind::PipelineRoot {
2810 is_root_pipeline: true,
2811 },
2812 LayoutVector2D::zero(),
2813 APZScrollGeneration::default(),
2814 HasScrollLinkedEffect::No,
2815 );
2816 }
2817
2818 fn add_image_mask_clip_node(
2819 &mut self,
2820 new_node_id: ClipId,
2821 spatial_id: SpatialId,
2822 image_mask: &ImageMask,
2823 fill_rule: FillRule,
2824 points_range: ItemRange<LayoutPoint>,
2825 ) {
2826 let spatial_node_index = self.get_space(spatial_id);
2827
2828 let snapped_mask_rect = self.normalize_rect_scroll_offset(
2829 &image_mask.rect,
2830 spatial_node_index,
2831 );
2832
2833 let points: Vec<LayoutPoint> = points_range.iter().collect();
2834
2835 let mut polygon_handle: Option<PolygonDataHandle> = None;
2837 if points.len() > 0 {
2838 let item = PolygonKey::new(&points, fill_rule);
2839
2840 let handle = self
2841 .interners
2842 .polygon
2843 .intern(&item, || item);
2844 polygon_handle = Some(handle);
2845 }
2846
2847 let item = ClipItemKey {
2848 kind: ClipItemKeyKind::image_mask(image_mask, polygon_handle),
2849 };
2850
2851 let handle = self
2852 .interners
2853 .clip
2854 .intern(&item, || {
2855 ClipInternData {
2856 key: item,
2857 }
2858 });
2859
2860 self.clip_tree_builder.define_image_mask_clip(
2861 new_node_id,
2862 handle,
2863 spatial_node_index,
2864 snapped_mask_rect,
2865 );
2866 }
2867
2868 fn add_rect_clip_node(
2870 &mut self,
2871 new_node_id: ClipId,
2872 spatial_id: SpatialId,
2873 clip_rect: &LayoutRect,
2874 ) {
2875 let spatial_node_index = self.get_space(spatial_id);
2876
2877 let snapped_clip_rect = self.normalize_rect_scroll_offset(
2878 clip_rect,
2879 spatial_node_index,
2880 );
2881
2882 let item = ClipItemKey {
2883 kind: ClipItemKeyKind::rectangle(ClipMode::Clip),
2884 };
2885 let handle = self
2886 .interners
2887 .clip
2888 .intern(&item, || {
2889 ClipInternData {
2890 key: item,
2891 }
2892 });
2893
2894 self.clip_tree_builder.define_rect_clip(
2895 new_node_id,
2896 handle,
2897 spatial_node_index,
2898 snapped_clip_rect,
2899 );
2900 }
2901
2902 fn add_rounded_rect_clip_node(
2903 &mut self,
2904 new_node_id: ClipId,
2905 spatial_id: SpatialId,
2906 clip: &ComplexClipRegion,
2907 ) {
2908 let spatial_node_index = self.get_space(spatial_id);
2909
2910 let snapped_region_rect = self.normalize_rect_scroll_offset(
2911 &clip.rect,
2912 spatial_node_index,
2913 );
2914
2915 let item = ClipItemKey {
2916 kind: ClipItemKeyKind::rounded_rect(
2917 clip.radii,
2918 clip.mode,
2919 ),
2920 };
2921
2922 let handle = self
2923 .interners
2924 .clip
2925 .intern(&item, || {
2926 ClipInternData {
2927 key: item,
2928 }
2929 });
2930
2931 self.clip_tree_builder.define_rounded_rect_clip(
2932 new_node_id,
2933 handle,
2934 spatial_node_index,
2935 snapped_region_rect,
2936 );
2937 }
2938
2939 pub fn add_scroll_frame(
2940 &mut self,
2941 new_node_id: SpatialId,
2942 parent_node_index: SpatialNodeIndex,
2943 external_id: ExternalScrollId,
2944 pipeline_id: PipelineId,
2945 frame_rect: &LayoutRect,
2946 content_size: &LayoutSize,
2947 frame_kind: ScrollFrameKind,
2948 external_scroll_offset: LayoutVector2D,
2949 scroll_offset_generation: APZScrollGeneration,
2950 has_scroll_linked_effect: HasScrollLinkedEffect,
2951 ) -> SpatialNodeIndex {
2952 let node_index = self.spatial_tree.add_scroll_frame(
2953 parent_node_index,
2954 external_id,
2955 pipeline_id,
2956 frame_rect,
2957 content_size,
2958 frame_kind,
2959 external_scroll_offset,
2960 scroll_offset_generation,
2961 has_scroll_linked_effect,
2962 );
2963 self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(new_node_id, node_index);
2964 node_index
2965 }
2966
2967 pub fn push_shadow(
2968 &mut self,
2969 shadow: Shadow,
2970 spatial_node_index: SpatialNodeIndex,
2971 clip_chain_id: api::ClipChainId,
2972 should_inflate: bool,
2973 ) {
2974 self.clip_tree_builder.push_clip_chain(Some(clip_chain_id), false, false);
2975
2976 self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
2979 shadow,
2980 spatial_node_index,
2981 should_inflate,
2982 }));
2983 }
2984
2985 pub fn pop_all_shadows(
2986 &mut self,
2987 ) {
2988 assert!(!self.pending_shadow_items.is_empty(), "popped shadows, but none were present");
2989
2990 let mut items = mem::replace(&mut self.pending_shadow_items, VecDeque::new());
2991
2992 while let Some(item) = items.pop_front() {
3006 match item {
3007 ShadowItem::Shadow(pending_shadow) => {
3008 let std_deviation = pending_shadow.shadow.blur_radius * 0.5;
3012
3013 let mut prim_list = PrimitiveList::empty();
3016 let blur_filter = Filter::Blur {
3017 width: std_deviation,
3018 height: std_deviation,
3019 should_inflate: pending_shadow.should_inflate,
3020 edge_mode: BlurEdgeMode::Duplicate,
3021 };
3022 let blur_is_noop = blur_filter.is_noop();
3023
3024 for item in &items {
3025 let (instance, info, spatial_node_index) = match item {
3026 ShadowItem::Image(ref pending_image) => {
3027 self.create_shadow_prim(
3028 &pending_shadow,
3029 pending_image,
3030 blur_is_noop,
3031 )
3032 }
3033 ShadowItem::LineDecoration(ref pending_line_dec) => {
3034 self.create_shadow_prim(
3035 &pending_shadow,
3036 pending_line_dec,
3037 blur_is_noop,
3038 )
3039 }
3040 ShadowItem::NormalBorder(ref pending_border) => {
3041 self.create_shadow_prim(
3042 &pending_shadow,
3043 pending_border,
3044 blur_is_noop,
3045 )
3046 }
3047 ShadowItem::Primitive(ref pending_primitive) => {
3048 self.create_shadow_prim(
3049 &pending_shadow,
3050 pending_primitive,
3051 blur_is_noop,
3052 )
3053 }
3054 ShadowItem::TextRun(ref pending_text_run) => {
3055 self.create_shadow_prim(
3056 &pending_shadow,
3057 pending_text_run,
3058 blur_is_noop,
3059 )
3060 }
3061 _ => {
3062 continue;
3063 }
3064 };
3065
3066 if blur_is_noop {
3067 self.add_primitive_to_draw_list(
3068 instance,
3069 info.rect,
3070 spatial_node_index,
3071 info.flags,
3072 );
3073 } else {
3074 prim_list.add_prim(
3075 instance,
3076 info.rect,
3077 spatial_node_index,
3078 info.flags,
3079 &mut self.prim_instances,
3080 &self.clip_tree_builder,
3081 );
3082 }
3083 }
3084
3085 if !prim_list.is_empty() {
3088 assert!(!blur_filter.is_noop());
3093 let composite_mode = Some(PictureCompositeMode::Filter(blur_filter));
3094 let composite_mode_key = composite_mode.clone().into();
3095 let raster_space = RasterSpace::Screen;
3096
3097 let shadow_pic_index = PictureIndex(self.prim_store.pictures
3099 .alloc()
3100 .init(PictureInstance::new_image(
3101 composite_mode,
3102 Picture3DContext::Out,
3103 PrimitiveFlags::IS_BACKFACE_VISIBLE,
3104 prim_list,
3105 pending_shadow.spatial_node_index,
3106 raster_space,
3107 PictureFlags::empty(),
3108 None,
3109 ))
3110 );
3111
3112 let shadow_pic_key = PictureKey::new(
3113 Picture { composite_mode_key, raster_space },
3114 );
3115
3116 let shadow_prim_data_handle = self.interners
3117 .picture
3118 .intern(&shadow_pic_key, || ());
3119
3120 let clip_node_id = self.clip_tree_builder.build_clip_set(api::ClipChainId::INVALID);
3121
3122 let shadow_prim_instance = PrimitiveInstance::new(
3123 PrimitiveKind::Picture {
3124 data_handle: shadow_prim_data_handle,
3125 pic_index: shadow_pic_index,
3126 },
3127 self.clip_tree_builder.build_for_picture(clip_node_id),
3128 LayoutRect::zero(),
3129 );
3130
3131 self.add_primitive_to_draw_list(
3134 shadow_prim_instance,
3135 LayoutRect::zero(),
3136 pending_shadow.spatial_node_index,
3137 PrimitiveFlags::IS_BACKFACE_VISIBLE,
3138 );
3139 }
3140
3141 self.clip_tree_builder.pop_clip();
3142 }
3143 ShadowItem::Image(pending_image) => {
3144 self.add_shadow_prim_to_draw_list(
3145 pending_image,
3146 )
3147 },
3148 ShadowItem::LineDecoration(pending_line_dec) => {
3149 self.add_shadow_prim_to_draw_list(
3150 pending_line_dec,
3151 )
3152 },
3153 ShadowItem::NormalBorder(pending_border) => {
3154 self.add_shadow_prim_to_draw_list(
3155 pending_border,
3156 )
3157 },
3158 ShadowItem::Primitive(pending_primitive) => {
3159 self.add_shadow_prim_to_draw_list(
3160 pending_primitive,
3161 )
3162 },
3163 ShadowItem::TextRun(pending_text_run) => {
3164 self.add_shadow_prim_to_draw_list(
3165 pending_text_run,
3166 )
3167 },
3168 }
3169 }
3170
3171 debug_assert!(items.is_empty());
3172 self.pending_shadow_items = items;
3173 }
3174
3175 fn create_shadow_prim<P>(
3176 &mut self,
3177 pending_shadow: &PendingShadow,
3178 pending_primitive: &PendingPrimitive<P>,
3179 blur_is_noop: bool,
3180 ) -> (PrimitiveInstance, LayoutPrimitiveInfo, SpatialNodeIndex)
3181 where
3182 P: InternablePrimitive + CreateShadow,
3183 Interners: AsMut<Interner<P>>,
3184 {
3185 let mut info = pending_primitive.info.clone();
3191 info.rect = info.rect.translate(pending_shadow.shadow.offset);
3192 info.clip_rect = info.clip_rect.translate(pending_shadow.shadow.offset);
3193
3194 let clip_set = self.clip_tree_builder.build_for_prim(
3195 pending_primitive.clip_node_id,
3196 &info,
3197 &[],
3198 &mut self.interners,
3199 );
3200
3201 let shadow_prim_instance = self.create_primitive(
3203 &info,
3204 clip_set,
3205 pending_primitive.prim.create_shadow(
3206 &pending_shadow.shadow,
3207 blur_is_noop,
3208 self.raster_space_stack.last().cloned().unwrap(),
3209 ),
3210 );
3211
3212 (shadow_prim_instance, info, pending_primitive.spatial_node_index)
3213 }
3214
3215 fn add_shadow_prim_to_draw_list<P>(
3216 &mut self,
3217 pending_primitive: PendingPrimitive<P>,
3218 ) where
3219 P: InternablePrimitive + IsVisible,
3220 Interners: AsMut<Interner<P>>,
3221 {
3222 if pending_primitive.prim.is_visible() {
3225 let clip_set = self.clip_tree_builder.build_for_prim(
3226 pending_primitive.clip_node_id,
3227 &pending_primitive.info,
3228 &[],
3229 &mut self.interners,
3230 );
3231
3232 self.add_prim_to_draw_list(
3233 &pending_primitive.info,
3234 pending_primitive.spatial_node_index,
3235 clip_set,
3236 pending_primitive.prim,
3237 );
3238 }
3239 }
3240
3241 pub fn add_line(
3242 &mut self,
3243 spatial_node_index: SpatialNodeIndex,
3244 clip_node_id: ClipNodeId,
3245 info: &LayoutPrimitiveInfo,
3246 wavy_line_thickness: f32,
3247 orientation: LineOrientation,
3248 color: ColorF,
3249 style: LineStyle,
3250 ) {
3251 self.add_primitive(
3252 spatial_node_index,
3253 clip_node_id,
3254 &info,
3255 Vec::new(),
3256 LineDecoration {
3257 style,
3258 orientation,
3259 wavy_line_thickness: Au::from_f32_px(wavy_line_thickness),
3260 color: color.into(),
3261 },
3262 );
3263 }
3264
3265 pub fn add_border(
3266 &mut self,
3267 spatial_node_index: SpatialNodeIndex,
3268 clip_node_id: ClipNodeId,
3269 info: &LayoutPrimitiveInfo,
3270 border_item: &BorderDisplayItem,
3271 gradient_stops: ItemRange<GradientStop>,
3272 ) {
3273 match border_item.details {
3274 BorderDetails::NinePatch(ref border) => {
3275 let nine_patch = NinePatchDescriptor {
3276 width: border.width,
3277 height: border.height,
3278 slice: border.slice,
3279 fill: border.fill,
3280 repeat_horizontal: border.repeat_horizontal,
3281 repeat_vertical: border.repeat_vertical,
3282 widths: border_item.widths.into(),
3283 };
3284
3285 match border.source {
3286 NinePatchBorderSource::Image(key, rendering) => {
3287 let prim = ImageBorder {
3288 request: ImageRequest {
3289 key,
3290 rendering,
3291 tile: None,
3292 },
3293 nine_patch,
3294 };
3295
3296 self.add_nonshadowable_primitive(
3297 spatial_node_index,
3298 clip_node_id,
3299 info,
3300 Vec::new(),
3301 prim,
3302 );
3303 }
3304 NinePatchBorderSource::Gradient(gradient) => {
3305 let prim = match self.create_linear_gradient_prim(
3306 &info,
3307 gradient.start_point,
3308 gradient.end_point,
3309 read_gradient_stops(gradient_stops),
3310 gradient.extend_mode,
3311 LayoutSize::new(border.height as f32, border.width as f32),
3312 LayoutSize::zero(),
3313 Some(Box::new(nine_patch)),
3314 EdgeMask::all(),
3315 ) {
3316 Some(prim) => prim,
3317 None => return,
3318 };
3319
3320 self.add_nonshadowable_primitive(
3321 spatial_node_index,
3322 clip_node_id,
3323 info,
3324 Vec::new(),
3325 prim,
3326 );
3327 }
3328 NinePatchBorderSource::RadialGradient(gradient) => {
3329 let prim = self.create_radial_gradient_prim(
3330 &info,
3331 gradient.center,
3332 gradient.start_offset * gradient.radius.width,
3333 gradient.end_offset * gradient.radius.width,
3334 gradient.radius.width / gradient.radius.height,
3335 read_gradient_stops(gradient_stops),
3336 gradient.extend_mode,
3337 LayoutSize::new(border.height as f32, border.width as f32),
3338 LayoutSize::zero(),
3339 Some(Box::new(nine_patch)),
3340 );
3341
3342 self.add_nonshadowable_primitive(
3343 spatial_node_index,
3344 clip_node_id,
3345 info,
3346 Vec::new(),
3347 prim,
3348 );
3349 }
3350 NinePatchBorderSource::ConicGradient(gradient) => {
3351 let prim = self.create_conic_gradient_prim(
3352 &info,
3353 gradient.center,
3354 gradient.angle,
3355 gradient.start_offset,
3356 gradient.end_offset,
3357 gradient_stops,
3358 gradient.extend_mode,
3359 LayoutSize::new(border.height as f32, border.width as f32),
3360 LayoutSize::zero(),
3361 Some(Box::new(nine_patch)),
3362 );
3363
3364 self.add_nonshadowable_primitive(
3365 spatial_node_index,
3366 clip_node_id,
3367 info,
3368 Vec::new(),
3369 prim,
3370 );
3371 }
3372 };
3373 }
3374 BorderDetails::Normal(ref border) => {
3375 self.add_normal_border(
3376 info,
3377 border,
3378 border_item.widths,
3379 spatial_node_index,
3380 clip_node_id,
3381 );
3382 }
3383 }
3384 }
3385
3386 pub fn create_linear_gradient_prim(
3387 &mut self,
3388 info: &LayoutPrimitiveInfo,
3389 start_point: LayoutPoint,
3390 end_point: LayoutPoint,
3391 stops: Vec<GradientStopKey>,
3392 extend_mode: ExtendMode,
3393 stretch_size: LayoutSize,
3394 mut tile_spacing: LayoutSize,
3395 nine_patch: Option<Box<NinePatchDescriptor>>,
3396 edge_aa_mask: EdgeMask,
3397 ) -> Option<LinearGradient> {
3398 let mut prim_rect = info.rect;
3399 simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3400
3401 let mut is_entirely_transparent = true;
3402 for stop in &stops {
3403 if stop.color.a > 0 {
3404 is_entirely_transparent = false;
3405 }
3406 }
3407
3408 if is_entirely_transparent {
3411 return None;
3412 }
3413
3414 let reverse_stops = start_point.x > end_point.x ||
3421 (start_point.x == end_point.x && start_point.y > end_point.y);
3422
3423 let (sp, ep) = if reverse_stops {
3427 (end_point, start_point)
3428 } else {
3429 (start_point, end_point)
3430 };
3431
3432 let stretch_ratio = compute_stretch_ratio(stretch_size, info.rect.size());
3433
3434 Some(LinearGradient {
3435 extend_mode,
3436 start_point: sp.into(),
3437 end_point: ep.into(),
3438 stretch_ratio: stretch_ratio.into(),
3439 tile_spacing: tile_spacing.into(),
3440 stops,
3441 reverse_stops,
3442 nine_patch,
3443 edge_aa_mask,
3444 enable_dithering: self.config.enable_dithering,
3445 })
3446 }
3447
3448 pub fn create_radial_gradient_prim(
3449 &mut self,
3450 info: &LayoutPrimitiveInfo,
3451 center: LayoutPoint,
3452 start_radius: f32,
3453 end_radius: f32,
3454 ratio_xy: f32,
3455 stops: Vec<GradientStopKey>,
3456 extend_mode: ExtendMode,
3457 stretch_size: LayoutSize,
3458 mut tile_spacing: LayoutSize,
3459 nine_patch: Option<Box<NinePatchDescriptor>>,
3460 ) -> RadialGradient {
3461 let mut prim_rect = info.rect;
3462 simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3463
3464 let params = RadialGradientParams {
3465 start_radius,
3466 end_radius,
3467 ratio_xy,
3468 };
3469
3470 let stretch_ratio = compute_stretch_ratio(stretch_size, info.rect.size());
3471
3472 RadialGradient {
3473 extend_mode,
3474 center: center.into(),
3475 params,
3476 stretch_ratio: stretch_ratio.into(),
3477 tile_spacing: tile_spacing.into(),
3478 nine_patch,
3479 stops,
3480 }
3481 }
3482
3483 pub fn create_conic_gradient_prim(
3484 &mut self,
3485 info: &LayoutPrimitiveInfo,
3486 center: LayoutPoint,
3487 angle: f32,
3488 start_offset: f32,
3489 end_offset: f32,
3490 stops: ItemRange<GradientStop>,
3491 extend_mode: ExtendMode,
3492 stretch_size: LayoutSize,
3493 mut tile_spacing: LayoutSize,
3494 nine_patch: Option<Box<NinePatchDescriptor>>,
3495 ) -> ConicGradient {
3496 let mut prim_rect = info.rect;
3497 simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3498
3499 let stops = stops.iter().map(|stop| {
3500 GradientStopKey {
3501 offset: stop.offset,
3502 color: stop.color.into(),
3503 }
3504 }).collect();
3505
3506 let stretch_ratio = compute_stretch_ratio(stretch_size, info.rect.size());
3507
3508 ConicGradient {
3509 extend_mode,
3510 center: center.into(),
3511 params: ConicGradientParams { angle, start_offset, end_offset },
3512 stretch_ratio: stretch_ratio.into(),
3513 tile_spacing: tile_spacing.into(),
3514 nine_patch,
3515 stops,
3516 }
3517 }
3518
3519 pub fn add_text(
3520 &mut self,
3521 spatial_node_index: SpatialNodeIndex,
3522 clip_node_id: ClipNodeId,
3523 prim_info: &LayoutPrimitiveInfo,
3524 font_instance_key: &FontInstanceKey,
3525 text_color: &ColorF,
3526 glyph_range: ItemRange<GlyphInstance>,
3527 glyph_options: Option<GlyphOptions>,
3528 ) {
3529 let offset = self.current_external_scroll_offset(spatial_node_index);
3530
3531 let text_run = {
3532 let shared_key = self.fonts.instance_keys.map_key(font_instance_key);
3533 let font_instance = match self.fonts.instances.get_font_instance(shared_key) {
3534 Some(instance) => instance,
3535 None => {
3536 warn!("Unknown font instance key");
3537 debug!("key={:?} shared={:?}", font_instance_key, shared_key);
3538 return;
3539 }
3540 };
3541
3542 if font_instance.size <= FontSize::zero() {
3544 return;
3545 }
3546
3547 let mut render_mode = self.config
3551 .default_font_render_mode
3552 .limit_by(font_instance.render_mode);
3553 let mut flags = font_instance.flags;
3554 if let Some(options) = glyph_options {
3555 render_mode = render_mode.limit_by(options.render_mode);
3556 flags |= options.flags;
3557 }
3558
3559 let font = FontInstance::new(
3560 font_instance,
3561 (*text_color).into(),
3562 render_mode,
3563 flags,
3564 );
3565
3566 let prim_origin = prim_info.rect.min.to_vector();
3580 let glyphs = glyph_range
3581 .iter()
3582 .map(|glyph| {
3583 GlyphInstance {
3584 index: glyph.index,
3585 point: glyph.point + offset - prim_origin,
3586 }
3587 })
3588 .collect();
3589
3590 let requested_raster_space = self.raster_space_stack
3593 .last()
3594 .cloned()
3595 .unwrap();
3596
3597 TextRun {
3598 glyphs,
3599 font,
3600 shadow: false,
3601 requested_raster_space,
3602 }
3603 };
3604
3605 self.add_primitive(
3606 spatial_node_index,
3607 clip_node_id,
3608 prim_info,
3609 Vec::new(),
3610 text_run,
3611 );
3612 }
3613
3614 pub fn add_image(
3615 &mut self,
3616 spatial_node_index: SpatialNodeIndex,
3617 clip_node_id: ClipNodeId,
3618 info: &LayoutPrimitiveInfo,
3619 stretch_size: StretchSizeKey,
3620 mut tile_spacing: LayoutSize,
3621 image_key: ImageKey,
3622 image_rendering: ImageRendering,
3623 alpha_type: AlphaType,
3624 color: ColorF,
3625 ) {
3626 let mut prim_rect = info.rect;
3627 let prim_size = prim_rect.size();
3630 let stored: LayoutSize = stretch_size.size.into();
3631 let stretch_size_for_simplify = LayoutSize::new(
3632 if stretch_size.fills_width { prim_size.width } else { stored.width },
3633 if stretch_size.fills_height { prim_size.height } else { stored.height },
3634 );
3635 simplify_repeated_primitive(&stretch_size_for_simplify, &mut tile_spacing, &mut prim_rect);
3636 let info = LayoutPrimitiveInfo {
3637 rect: prim_rect,
3638 .. *info
3639 };
3640
3641 self.add_primitive(
3642 spatial_node_index,
3643 clip_node_id,
3644 &info,
3645 Vec::new(),
3646 Image {
3647 key: image_key,
3648 tile_spacing: tile_spacing.into(),
3649 stretch_size,
3650 color: color.into(),
3651 image_rendering,
3652 alpha_type,
3653 },
3654 );
3655 }
3656
3657 pub fn add_yuv_image(
3658 &mut self,
3659 spatial_node_index: SpatialNodeIndex,
3660 clip_node_id: ClipNodeId,
3661 info: &LayoutPrimitiveInfo,
3662 yuv_data: YuvData,
3663 color_depth: ColorDepth,
3664 color_space: YuvColorSpace,
3665 color_range: ColorRange,
3666 image_rendering: ImageRendering,
3667 ) {
3668 let format = yuv_data.get_format();
3669 let yuv_key = match yuv_data {
3670 YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3671 YuvData::P010(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3672 YuvData::NV16(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3673 YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
3674 YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
3675 };
3676
3677 self.add_nonshadowable_primitive(
3678 spatial_node_index,
3679 clip_node_id,
3680 info,
3681 Vec::new(),
3682 YuvImage {
3683 color_depth,
3684 yuv_key,
3685 format,
3686 color_space,
3687 color_range,
3688 image_rendering,
3689 },
3690 );
3691 }
3692
3693 fn add_primitive_instance_to_3d_root(
3694 &mut self,
3695 prim: ExtendedPrimitiveInstance,
3696 ) {
3697 for sc in self.sc_stack.iter_mut().rev() {
3699 match sc.context_3d {
3700 Picture3DContext::In { root_data: Some(ref mut prims), .. } => {
3701 prims.push(prim);
3702 break;
3703 }
3704 Picture3DContext::In { .. } => {}
3705 Picture3DContext::Out => panic!("Unable to find 3D root"),
3706 }
3707 }
3708 }
3709
3710 #[allow(dead_code)]
3711 pub fn add_backdrop_filter(
3712 &mut self,
3713 spatial_node_index: SpatialNodeIndex,
3714 clip_node_id: ClipNodeId,
3715 info: &LayoutPrimitiveInfo,
3716 filters: Vec<Filter>,
3717 filter_datas: Vec<FilterData>,
3718 ) {
3719 let filter_spatial_node_index = SpatialNodeIndex::UNKNOWN;
3723
3724 self.make_current_slice_atomic_if_required();
3725
3726 let clip_leaf_id = self.clip_tree_builder.build_for_prim(
3730 clip_node_id,
3731 info,
3732 &[],
3733 &mut self.interners,
3734 );
3735
3736 let backdrop_capture_instance = self.create_primitive(
3739 info,
3740 clip_leaf_id,
3741 BackdropCapture {
3742 },
3743 );
3744
3745 let mut prim_list = PrimitiveList::empty();
3748 prim_list.add_prim(
3749 backdrop_capture_instance,
3750 info.rect,
3751 spatial_node_index,
3752 info.flags,
3753 &mut self.prim_instances,
3754 &self.clip_tree_builder,
3755 );
3756
3757 let mut source = PictureChainBuilder::from_prim_list(
3758 prim_list,
3759 info.flags,
3760 filter_spatial_node_index,
3761 RasterSpace::Screen,
3762 true,
3763 );
3764
3765 source = self.wrap_prim_with_filters(
3768 source,
3769 clip_node_id,
3770 filters,
3771 filter_datas,
3772 true,
3773 LayoutVector2D::zero(),
3774 );
3775
3776 if source.has_picture() {
3779 source = source.add_picture(
3780 PictureCompositeMode::IntermediateSurface,
3781 clip_node_id,
3782 Picture3DContext::Out,
3783 &mut self.interners,
3784 &mut self.prim_store,
3785 &mut self.prim_instances,
3786 &mut self.clip_tree_builder,
3787 );
3788
3789 let filtered_instance = source.finalize(
3790 clip_node_id,
3791 &mut self.interners,
3792 &mut self.prim_store,
3793 &mut self.clip_tree_builder,
3794 None,
3795 );
3796
3797 let output_pic_index = match filtered_instance.kind {
3800 PrimitiveKind::Picture { pic_index, .. } => pic_index,
3801 _ => panic!("bug: not a picture"),
3802 };
3803
3804 let sc_index = self.sc_stack.iter().rposition(|sc| {
3807 !sc.flags.contains(StackingContextFlags::WRAPS_BACKDROP_FILTER)
3808 });
3809
3810 match sc_index {
3811 Some(sc_index) => {
3812 self.sc_stack[sc_index].prim_list.add_prim(
3813 filtered_instance,
3814 info.rect,
3815 filter_spatial_node_index,
3816 info.flags,
3817 &mut self.prim_instances,
3818 &self.clip_tree_builder,
3819 );
3820 }
3821 None => {
3822 self.tile_cache_builder.add_prim(
3823 filtered_instance,
3824 info.rect,
3825 filter_spatial_node_index,
3826 info.flags,
3827 self.spatial_tree,
3828 &self.quality_settings,
3829 &mut self.prim_instances,
3830 &self.clip_tree_builder,
3831 );
3832 }
3833 }
3834
3835 let mut backdrop_render_instance = self.create_primitive(
3837 info,
3838 clip_leaf_id,
3839 BackdropRender {
3840 },
3841 );
3842
3843 match backdrop_render_instance.kind {
3846 PrimitiveKind::BackdropRender { ref mut pic_index, .. } => {
3847 assert_eq!(*pic_index, PictureIndex::INVALID);
3848 *pic_index = output_pic_index;
3849 }
3850 _ => panic!("bug: unexpected prim kind"),
3851 }
3852
3853 self.add_primitive_to_draw_list(
3854 backdrop_render_instance,
3855 info.rect,
3856 spatial_node_index,
3857 info.flags,
3858 );
3859 }
3860 }
3861
3862 #[must_use]
3863 fn wrap_prim_with_filters(
3864 &mut self,
3865 mut source: PictureChainBuilder,
3866 clip_node_id: ClipNodeId,
3867 mut filter_ops: Vec<Filter>,
3868 filter_datas: Vec<FilterData>,
3869 is_backdrop_filter: bool,
3870 context_offset: LayoutVector2D,
3871 ) -> PictureChainBuilder {
3872 let mut current_filter_data_index = 0;
3874 if let Some(Filter::SVGGraphNode(..)) = filter_ops.first() {
3879 const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX;
3888 const SVGFE_INFLATE: i16 = 1;
3891
3892 let mut reference_for_buffer_id: [FilterGraphPictureReference; BUFFER_LIMIT] = [
3912 FilterGraphPictureReference{
3913 buffer_id: FilterOpGraphPictureBufferId::BufferId(-1),
3917 subregion: LayoutRect::zero(), offset: LayoutVector2D::zero(),
3919 inflate: 0,
3920 source_padding: LayoutRect::zero(),
3921 target_padding: LayoutRect::zero(),
3922 }; BUFFER_LIMIT];
3923 let mut filters: Vec<(FilterGraphNode, FilterGraphOp)> = Vec::new();
3924 filters.reserve(BUFFER_LIMIT);
3925 for (original_id, parsefilter) in filter_ops.iter().enumerate() {
3926 if filters.len() >= BUFFER_LIMIT {
3927 return source;
3930 }
3931
3932 let newfilter = match parsefilter {
3933 Filter::SVGGraphNode(parsenode, op) => {
3934 let clip_region = parsenode.subregion
3938 .translate(context_offset);
3939
3940 let mut newnode = FilterGraphNode {
3941 kept_by_optimizer: false,
3942 linear: parsenode.linear,
3943 inflate: SVGFE_INFLATE,
3944 inputs: Vec::new(),
3945 subregion: clip_region,
3946 };
3947
3948 let mut remapped_inputs: Vec<FilterGraphPictureReference> = Vec::new();
3951 remapped_inputs.reserve_exact(parsenode.inputs.len());
3952 for input in &parsenode.inputs {
3953 match input.buffer_id {
3954 FilterOpGraphPictureBufferId::BufferId(buffer_id) => {
3955 let pic = *reference_for_buffer_id
3958 .get(buffer_id as usize)
3959 .expect("BufferId not valid?");
3960 let offset = input.offset;
3965 let subregion = pic.subregion
3966 .translate(offset);
3967 let source_padding = LayoutRect::zero()
3968 .translate(-offset);
3969 let target_padding = LayoutRect::zero()
3970 .translate(offset);
3971 remapped_inputs.push(
3972 FilterGraphPictureReference {
3973 buffer_id: pic.buffer_id,
3974 subregion,
3975 offset,
3976 inflate: pic.inflate,
3977 source_padding,
3978 target_padding,
3979 });
3980 }
3981 FilterOpGraphPictureBufferId::None => panic!("Unsupported FilterOpGraphPictureBufferId"),
3982 }
3983 }
3984
3985 fn union_unchecked(a: LayoutRect, b: LayoutRect) -> LayoutRect {
3986 let mut r = a;
3987 if r.min.x > b.min.x {r.min.x = b.min.x}
3988 if r.min.y > b.min.y {r.min.y = b.min.y}
3989 if r.max.x < b.max.x {r.max.x = b.max.x}
3990 if r.max.y < b.max.y {r.max.y = b.max.y}
3991 r
3992 }
3993
3994 match op {
3995 FilterGraphOp::SVGFEFlood{..} |
3996 FilterGraphOp::SVGFESourceAlpha |
3997 FilterGraphOp::SVGFESourceGraphic |
3998 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} |
3999 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} |
4000 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} |
4001 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {
4002 assert!(remapped_inputs.len() == 0);
4003 (newnode.clone(), op.clone())
4004 }
4005 FilterGraphOp::SVGFEColorMatrix{..} |
4006 FilterGraphOp::SVGFEIdentity |
4007 FilterGraphOp::SVGFEImage{..} |
4008 FilterGraphOp::SVGFEOpacity{..} |
4009 FilterGraphOp::SVGFEToAlpha => {
4010 assert!(remapped_inputs.len() == 1);
4011 newnode.inputs = remapped_inputs;
4012 (newnode.clone(), op.clone())
4013 }
4014 FilterGraphOp::SVGFEComponentTransfer => {
4015 assert!(remapped_inputs.len() == 1);
4016 let filter_data =
4018 &filter_datas[current_filter_data_index];
4019 let filter_data = filter_data.sanitize();
4020 current_filter_data_index = current_filter_data_index + 1;
4021
4022 let creates_pixels =
4037 if let Some(a) = filter_data.r_values.get(3) {
4038 *a >= (0.5/255.0)
4039 } else {
4040 false
4041 };
4042 let filter_data_key = SFilterDataKey {
4043 data:
4044 SFilterData {
4045 r_func: SFilterDataComponent::from_functype_values(
4046 filter_data.func_r_type, &filter_data.r_values),
4047 g_func: SFilterDataComponent::from_functype_values(
4048 filter_data.func_g_type, &filter_data.g_values),
4049 b_func: SFilterDataComponent::from_functype_values(
4050 filter_data.func_b_type, &filter_data.b_values),
4051 a_func: SFilterDataComponent::from_functype_values(
4052 filter_data.func_a_type, &filter_data.a_values),
4053 },
4054 };
4055
4056 let handle = self.interners
4057 .filter_data
4058 .intern(&filter_data_key, || ());
4059
4060 newnode.inputs = remapped_inputs;
4061 (newnode.clone(), FilterGraphOp::SVGFEComponentTransferInterned{handle, creates_pixels})
4062 }
4063 FilterGraphOp::SVGFEComponentTransferInterned{..} => unreachable!(),
4064 FilterGraphOp::SVGFETile => {
4065 assert!(remapped_inputs.len() == 1);
4066 remapped_inputs[0].source_padding =
4068 LayoutRect::max_rect();
4069 remapped_inputs[0].target_padding =
4070 LayoutRect::max_rect();
4071 newnode.inputs = remapped_inputs;
4072 (newnode.clone(), op.clone())
4073 }
4074 FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{kernel_unit_length_x, kernel_unit_length_y, ..} |
4075 FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{kernel_unit_length_x, kernel_unit_length_y, ..} |
4076 FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{kernel_unit_length_x, kernel_unit_length_y, ..} |
4077 FilterGraphOp::SVGFEMorphologyDilate{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
4078 assert!(remapped_inputs.len() == 1);
4079 let padding = LayoutSize::new(
4080 kernel_unit_length_x.ceil(),
4081 kernel_unit_length_y.ceil(),
4082 );
4083 remapped_inputs[0].source_padding =
4086 remapped_inputs[0].source_padding
4087 .inflate(padding.width, padding.height);
4088 remapped_inputs[0].target_padding =
4091 remapped_inputs[0].target_padding
4092 .inflate(padding.width, padding.height);
4093 newnode.inputs = remapped_inputs;
4094 (newnode.clone(), op.clone())
4095 },
4096 FilterGraphOp::SVGFEDiffuseLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
4097 FilterGraphOp::SVGFEDiffuseLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
4098 FilterGraphOp::SVGFEDiffuseLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
4099 FilterGraphOp::SVGFESpecularLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
4100 FilterGraphOp::SVGFESpecularLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
4101 FilterGraphOp::SVGFESpecularLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
4102 FilterGraphOp::SVGFEMorphologyErode{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
4103 assert!(remapped_inputs.len() == 1);
4104 let padding = LayoutSize::new(
4105 kernel_unit_length_x.ceil(),
4106 kernel_unit_length_y.ceil(),
4107 );
4108 remapped_inputs[0].source_padding =
4111 remapped_inputs[0].source_padding
4112 .inflate(padding.width, padding.height);
4113 remapped_inputs[0].target_padding =
4116 remapped_inputs[0].target_padding
4117 .inflate(padding.width, padding.height);
4118 newnode.inputs = remapped_inputs;
4119 (newnode.clone(), op.clone())
4120 },
4121 FilterGraphOp::SVGFEDisplacementMap { scale, .. } => {
4122 assert!(remapped_inputs.len() == 2);
4123 let padding = LayoutSize::new(
4124 scale.ceil(),
4125 scale.ceil(),
4126 );
4127 remapped_inputs[0].source_padding =
4132 remapped_inputs[0].source_padding
4133 .inflate(padding.width, padding.height);
4134 remapped_inputs[1].source_padding =
4135 remapped_inputs[1].source_padding
4136 .inflate(padding.width, padding.height);
4137 remapped_inputs[0].target_padding =
4138 remapped_inputs[0].target_padding
4139 .inflate(padding.width, padding.height);
4140 remapped_inputs[1].target_padding =
4141 remapped_inputs[1].target_padding
4142 .inflate(padding.width, padding.height);
4143 newnode.inputs = remapped_inputs;
4144 (newnode.clone(), op.clone())
4145 },
4146 FilterGraphOp::SVGFEDropShadow{ dx, dy, std_deviation_x, std_deviation_y, .. } => {
4147 assert!(remapped_inputs.len() == 1);
4148 let padding = LayoutSize::new(
4149 std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
4150 std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
4151 );
4152 remapped_inputs[0].source_padding =
4154 union_unchecked(
4155 remapped_inputs[0].source_padding,
4156 remapped_inputs[0].source_padding
4157 .inflate(padding.width, padding.height)
4158 .translate(
4159 LayoutVector2D::new(-dx, -dy)
4160 )
4161 );
4162 remapped_inputs[0].target_padding =
4165 union_unchecked(
4166 remapped_inputs[0].target_padding,
4167 remapped_inputs[0].target_padding
4168 .inflate(padding.width, padding.height)
4169 .translate(
4170 LayoutVector2D::new(*dx, *dy)
4171 )
4172 );
4173 newnode.inputs = remapped_inputs;
4174 (newnode.clone(), op.clone())
4175 },
4176 FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} => {
4177 assert!(remapped_inputs.len() == 1);
4178 let padding = LayoutSize::new(
4179 std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
4180 std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
4181 );
4182 remapped_inputs[0].source_padding =
4184 remapped_inputs[0].source_padding
4185 .inflate(padding.width, padding.height);
4186 remapped_inputs[0].target_padding =
4188 remapped_inputs[0].target_padding
4189 .inflate(padding.width, padding.height);
4190 newnode.inputs = remapped_inputs;
4191 (newnode.clone(), op.clone())
4192 }
4193 FilterGraphOp::SVGFEBlendColor |
4194 FilterGraphOp::SVGFEBlendColorBurn |
4195 FilterGraphOp::SVGFEBlendColorDodge |
4196 FilterGraphOp::SVGFEBlendDarken |
4197 FilterGraphOp::SVGFEBlendDifference |
4198 FilterGraphOp::SVGFEBlendExclusion |
4199 FilterGraphOp::SVGFEBlendHardLight |
4200 FilterGraphOp::SVGFEBlendHue |
4201 FilterGraphOp::SVGFEBlendLighten |
4202 FilterGraphOp::SVGFEBlendLuminosity|
4203 FilterGraphOp::SVGFEBlendMultiply |
4204 FilterGraphOp::SVGFEBlendNormal |
4205 FilterGraphOp::SVGFEBlendOverlay |
4206 FilterGraphOp::SVGFEBlendSaturation |
4207 FilterGraphOp::SVGFEBlendScreen |
4208 FilterGraphOp::SVGFEBlendSoftLight |
4209 FilterGraphOp::SVGFECompositeArithmetic{..} |
4210 FilterGraphOp::SVGFECompositeATop |
4211 FilterGraphOp::SVGFECompositeIn |
4212 FilterGraphOp::SVGFECompositeLighter |
4213 FilterGraphOp::SVGFECompositeOut |
4214 FilterGraphOp::SVGFECompositeOver |
4215 FilterGraphOp::SVGFECompositeXOR => {
4216 assert!(remapped_inputs.len() == 2);
4217 newnode.inputs = remapped_inputs;
4218 (newnode, op.clone())
4219 }
4220 }
4221 }
4222 Filter::Opacity(valuebinding, value) => {
4223 let pic = reference_for_buffer_id[original_id as usize - 1];
4229 (
4230 FilterGraphNode {
4231 kept_by_optimizer: false,
4232 linear: false,
4233 inflate: SVGFE_INFLATE,
4234 inputs: [pic].to_vec(),
4235 subregion: pic.subregion,
4236 },
4237 FilterGraphOp::SVGFEOpacity{
4238 valuebinding: *valuebinding,
4239 value: *value,
4240 },
4241 )
4242 }
4243 _ => {
4244 log!(Level::Warn, "wrap_prim_with_filters: unexpected filter after SVG filters filter[{:?}]={:?}", original_id, parsefilter);
4245 return source;
4249 }
4250 };
4251 let id = filters.len();
4252 filters.push(newfilter);
4253
4254 reference_for_buffer_id[original_id] = FilterGraphPictureReference {
4257 buffer_id: FilterOpGraphPictureBufferId::BufferId(id as i16),
4258 subregion: filters[id].0.subregion,
4259 offset: LayoutVector2D::zero(),
4260 inflate: filters[id].0.inflate,
4261 source_padding: LayoutRect::zero(),
4262 target_padding: LayoutRect::zero(),
4263 };
4264 }
4265
4266 if filters.len() >= BUFFER_LIMIT {
4267 return source;
4270 }
4271
4272 let mut kept_node_by_buffer_id = [false; BUFFER_LIMIT];
4280 kept_node_by_buffer_id[filters.len() - 1] = true;
4281 for (index, (node, _op)) in filters.iter_mut().enumerate().rev() {
4282 let mut keep = false;
4283 if let Some(k) = kept_node_by_buffer_id.get(index) {
4285 if *k {
4286 keep = true;
4287 }
4288 }
4289 if keep {
4290 node.kept_by_optimizer = true;
4294 for input in &node.inputs {
4295 if let FilterOpGraphPictureBufferId::BufferId(id) = input.buffer_id {
4296 if let Some(k) = kept_node_by_buffer_id.get_mut(id as usize) {
4297 *k = true;
4298 }
4299 }
4300 }
4301 }
4302 }
4303
4304 let mut invalid_dag = false;
4307 for (id, (node, _op)) in filters.iter().enumerate() {
4308 for input in &node.inputs {
4309 if let FilterOpGraphPictureBufferId::BufferId(buffer_id) = input.buffer_id {
4310 if buffer_id < 0 || buffer_id as usize >= id {
4311 invalid_dag = true;
4312 }
4313 }
4314 }
4315 }
4316
4317 if invalid_dag {
4318 log!(Level::Warn, "List of FilterOp::SVGGraphNode filter primitives appears to be invalid!");
4319 for (id, (node, op)) in filters.iter().enumerate() {
4320 log!(Level::Warn, " node: buffer=BufferId({}) op={} inflate={} subregion {:?} linear={} kept={}",
4321 id, op.kind(), node.inflate,
4322 node.subregion,
4323 node.linear,
4324 node.kept_by_optimizer,
4325 );
4326 for input in &node.inputs {
4327 log!(Level::Warn, "input: buffer={} inflate={} subregion {:?} offset {:?} target_padding={:?} source_padding={:?}",
4328 match input.buffer_id {
4329 FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId({})", id),
4330 FilterOpGraphPictureBufferId::None => "None".into(),
4331 },
4332 input.inflate,
4333 input.subregion,
4334 input.offset,
4335 input.target_padding,
4336 input.source_padding,
4337 );
4338 }
4339 }
4340 }
4341 if invalid_dag {
4342 return source;
4344 }
4345
4346 let composite_mode = PictureCompositeMode::SVGFEGraph(
4347 filters,
4348 );
4349
4350 source = source.add_picture(
4351 composite_mode,
4352 clip_node_id,
4353 Picture3DContext::Out,
4354 &mut self.interners,
4355 &mut self.prim_store,
4356 &mut self.prim_instances,
4357 &mut self.clip_tree_builder,
4358 );
4359
4360 return source;
4361 }
4362
4363 for filter in &mut filter_ops {
4365 let composite_mode = match filter {
4366 Filter::ComponentTransfer => {
4367 let filter_data =
4368 &filter_datas[current_filter_data_index];
4369 let filter_data = filter_data.sanitize();
4370 current_filter_data_index = current_filter_data_index + 1;
4371 if filter_data.is_identity() {
4372 continue
4373 } else {
4374 let filter_data_key = SFilterDataKey {
4375 data:
4376 SFilterData {
4377 r_func: SFilterDataComponent::from_functype_values(
4378 filter_data.func_r_type, &filter_data.r_values),
4379 g_func: SFilterDataComponent::from_functype_values(
4380 filter_data.func_g_type, &filter_data.g_values),
4381 b_func: SFilterDataComponent::from_functype_values(
4382 filter_data.func_b_type, &filter_data.b_values),
4383 a_func: SFilterDataComponent::from_functype_values(
4384 filter_data.func_a_type, &filter_data.a_values),
4385 },
4386 };
4387
4388 let handle = self.interners
4389 .filter_data
4390 .intern(&filter_data_key, || ());
4391 PictureCompositeMode::ComponentTransferFilter(handle)
4392 }
4393 }
4394 Filter::SVGGraphNode(_, _) => {
4395 panic!("SVGGraphNode encountered in regular CSS filter chain?");
4397 }
4398 _ => {
4399 if filter.is_noop() {
4400 continue;
4401 } else {
4402 let mut filter = filter.clone();
4403
4404 if is_backdrop_filter {
4408 if let Filter::Blur { ref mut should_inflate, ref mut edge_mode, .. } = filter {
4409 *should_inflate = false;
4410 *edge_mode = BlurEdgeMode::Mirror;
4411 }
4412 }
4413
4414 PictureCompositeMode::Filter(filter)
4415 }
4416 }
4417 };
4418
4419 source = source.add_picture(
4420 composite_mode,
4421 clip_node_id,
4422 Picture3DContext::Out,
4423 &mut self.interners,
4424 &mut self.prim_store,
4425 &mut self.prim_instances,
4426 &mut self.clip_tree_builder,
4427 );
4428 }
4429
4430 source
4431 }
4432}
4433
4434
4435pub trait CreateShadow {
4436 fn create_shadow(
4437 &self,
4438 shadow: &Shadow,
4439 blur_is_noop: bool,
4440 current_raster_space: RasterSpace,
4441 ) -> Self;
4442}
4443
4444pub trait IsVisible {
4445 fn is_visible(&self) -> bool;
4446}
4447
4448struct ExtendedPrimitiveInstance {
4452 instance: PrimitiveInstance,
4453 spatial_node_index: SpatialNodeIndex,
4454 flags: PrimitiveFlags,
4455}
4456
4457struct StackingContextInfo {
4460 pop_containing_block: bool,
4462 pop_stacking_context: bool,
4464 set_tile_cache_barrier: bool,
4466 needs_extra_stacking_context: bool,
4470}
4471
4472struct FlattenedStackingContext {
4476 prim_list: PrimitiveList,
4478
4479 prim_flags: PrimitiveFlags,
4481
4482 spatial_node_index: SpatialNodeIndex,
4484
4485 clip_node_id: ClipNodeId,
4487
4488 composite_ops: CompositeOps,
4491
4492 blit_reason: BlitReason,
4495
4496 transform_style: TransformStyle,
4498
4499 context_3d: Picture3DContext<ExtendedPrimitiveInstance>,
4501
4502 flags: StackingContextFlags,
4504
4505 raster_space: RasterSpace,
4507}
4508
4509impl FlattenedStackingContext {
4510 pub fn is_3d(&self) -> bool {
4512 self.transform_style == TransformStyle::Preserve3D && self.composite_ops.is_empty()
4513 }
4514
4515 pub fn is_redundant(
4517 context_3d: &Picture3DContext<ExtendedPrimitiveInstance>,
4518 composite_ops: &CompositeOps,
4519 blit_reason: BlitReason,
4520 parent: Option<&FlattenedStackingContext>,
4521 prim_flags: PrimitiveFlags,
4522 ) -> bool {
4523 if let Picture3DContext::In { .. } = context_3d {
4525 return false;
4526 }
4527
4528 if composite_ops.has_valid_filters() {
4530 return false;
4531 }
4532
4533 if composite_ops.mix_blend_mode.is_some() {
4535 match parent {
4536 Some(ref parent) => {
4537 if !parent.prim_list.is_empty() {
4540 return false;
4541 }
4542 }
4543 None => {
4544 return false;
4548 }
4549 }
4550 }
4551
4552 if !blit_reason.is_empty() {
4554 return false;
4555 }
4556
4557 if !prim_flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE) {
4559 return false;
4560 }
4561
4562 true
4564 }
4565
4566 pub fn cut_item_sequence(
4568 &mut self,
4569 prim_store: &mut PrimitiveStore,
4570 interners: &mut Interners,
4571 composite_mode: Option<PictureCompositeMode>,
4572 flat_items_context_3d: Picture3DContext<OrderedPictureChild>,
4573 clip_tree_builder: &mut ClipTreeBuilder,
4574 ) -> Option<(PictureIndex, PrimitiveInstance)> {
4575 if self.prim_list.is_empty() {
4576 return None
4577 }
4578
4579 let pic_index = PictureIndex(prim_store.pictures
4580 .alloc()
4581 .init(PictureInstance::new_image(
4582 composite_mode.clone(),
4583 flat_items_context_3d,
4584 self.prim_flags,
4585 mem::replace(&mut self.prim_list, PrimitiveList::empty()),
4586 self.spatial_node_index,
4587 self.raster_space,
4588 PictureFlags::empty(),
4589 None
4590 ))
4591 );
4592
4593 let prim_instance = create_prim_instance(
4594 pic_index,
4595 composite_mode.into(),
4596 self.raster_space,
4597 self.clip_node_id,
4598 interners,
4599 clip_tree_builder,
4600 );
4601
4602 Some((pic_index, prim_instance))
4603 }
4604}
4605
4606pub struct PendingPrimitive<T> {
4610 spatial_node_index: SpatialNodeIndex,
4611 clip_node_id: ClipNodeId,
4612 info: LayoutPrimitiveInfo,
4613 prim: T,
4614}
4615
4616pub struct PendingShadow {
4619 shadow: Shadow,
4620 should_inflate: bool,
4621 spatial_node_index: SpatialNodeIndex,
4622}
4623
4624pub enum ShadowItem {
4625 Shadow(PendingShadow),
4626 Image(PendingPrimitive<Image>),
4627 LineDecoration(PendingPrimitive<LineDecoration>),
4628 NormalBorder(PendingPrimitive<NormalBorderPrim>),
4629 Primitive(PendingPrimitive<RectanglePrim>),
4630 TextRun(PendingPrimitive<TextRun>),
4631}
4632
4633impl From<PendingPrimitive<Image>> for ShadowItem {
4634 fn from(image: PendingPrimitive<Image>) -> Self {
4635 ShadowItem::Image(image)
4636 }
4637}
4638
4639impl From<PendingPrimitive<LineDecoration>> for ShadowItem {
4640 fn from(line_dec: PendingPrimitive<LineDecoration>) -> Self {
4641 ShadowItem::LineDecoration(line_dec)
4642 }
4643}
4644
4645impl From<PendingPrimitive<NormalBorderPrim>> for ShadowItem {
4646 fn from(border: PendingPrimitive<NormalBorderPrim>) -> Self {
4647 ShadowItem::NormalBorder(border)
4648 }
4649}
4650
4651impl From<PendingPrimitive<RectanglePrim>> for ShadowItem {
4652 fn from(container: PendingPrimitive<RectanglePrim>) -> Self {
4653 ShadowItem::Primitive(container)
4654 }
4655}
4656
4657impl From<PendingPrimitive<TextRun>> for ShadowItem {
4658 fn from(text_run: PendingPrimitive<TextRun>) -> Self {
4659 ShadowItem::TextRun(text_run)
4660 }
4661}
4662
4663fn create_prim_instance(
4664 pic_index: PictureIndex,
4665 composite_mode_key: PictureCompositeKey,
4666 raster_space: RasterSpace,
4667 clip_node_id: ClipNodeId,
4668 interners: &mut Interners,
4669 clip_tree_builder: &mut ClipTreeBuilder,
4670) -> PrimitiveInstance {
4671 let pic_key = PictureKey::new(
4672 Picture {
4673 composite_mode_key,
4674 raster_space,
4675 },
4676 );
4677
4678 let data_handle = interners
4679 .picture
4680 .intern(&pic_key, || ());
4681
4682 PrimitiveInstance::new(
4683 PrimitiveKind::Picture {
4684 data_handle,
4685 pic_index,
4686 },
4687 clip_tree_builder.build_for_picture(
4688 clip_node_id,
4689 ),
4690 LayoutRect::zero(),
4691 )
4692}
4693
4694fn filter_ops_for_compositing(
4695 input_filters: ItemRange<FilterOp>,
4696) -> Vec<Filter> {
4697 input_filters.iter().map(|filter| filter.into()).collect()
4701}
4702
4703fn filter_datas_for_compositing(
4704 input_filter_datas: &[TempFilterData],
4705) -> Vec<FilterData> {
4706 let mut filter_datas = vec![];
4710 for temp_filter_data in input_filter_datas {
4711 let func_types : Vec<ComponentTransferFuncType> = temp_filter_data.func_types.iter().collect();
4712 debug_assert!(func_types.len() == 4);
4713 filter_datas.push( FilterData {
4714 func_r_type: func_types[0],
4715 r_values: temp_filter_data.r_values.iter().collect(),
4716 func_g_type: func_types[1],
4717 g_values: temp_filter_data.g_values.iter().collect(),
4718 func_b_type: func_types[2],
4719 b_values: temp_filter_data.b_values.iter().collect(),
4720 func_a_type: func_types[3],
4721 a_values: temp_filter_data.a_values.iter().collect(),
4722 });
4723 }
4724 filter_datas
4725}
4726
4727fn process_image_stretch_size(
4736 unsnapped_rect: &LayoutRect,
4737 repeat_size: LayoutSize,
4738) -> StretchSizeKey {
4739 const EPSILON: f32 = 0.001;
4740 let fills_width = repeat_size.width.approx_eq_eps(&unsnapped_rect.width(), &EPSILON);
4741 let fills_height = repeat_size.height.approx_eq_eps(&unsnapped_rect.height(), &EPSILON);
4742 let stored = LayoutSize::new(
4745 if fills_width { 0.0 } else { repeat_size.width },
4746 if fills_height { 0.0 } else { repeat_size.height },
4747 );
4748 StretchSizeKey {
4749 size: stored.into(),
4750 fills_width,
4751 fills_height,
4752 }
4753}
4754
4755fn process_repeat_size(
4756 snapped_rect: &LayoutRect,
4757 unsnapped_rect: &LayoutRect,
4758 repeat_size: LayoutSize,
4759) -> LayoutSize {
4760 const EPSILON: f32 = 0.001;
4768 LayoutSize::new(
4769 if repeat_size.width.approx_eq_eps(&unsnapped_rect.width(), &EPSILON) {
4770 snapped_rect.width()
4771 } else {
4772 repeat_size.width
4773 },
4774 if repeat_size.height.approx_eq_eps(&unsnapped_rect.height(), &EPSILON) {
4775 snapped_rect.height()
4776 } else {
4777 repeat_size.height
4778 },
4779 )
4780}
4781
4782fn compute_stretch_ratio(stretch_size: LayoutSize, prim_size: LayoutSize) -> LayoutSize {
4795 let prim_ok = prim_size.width.is_finite()
4796 && prim_size.width > 0.0
4797 && prim_size.height.is_finite()
4798 && prim_size.height > 0.0;
4799 if !prim_ok {
4800 return LayoutSize::new(1.0, 1.0);
4801 }
4802 let w = (stretch_size.width / prim_size.width).min(1.0);
4803 let h = (stretch_size.height / prim_size.height).min(1.0);
4804 LayoutSize::new(w, h)
4805}
4806
4807fn read_gradient_stops(stops: ItemRange<GradientStop>) -> Vec<GradientStopKey> {
4808 stops.iter().map(|stop| {
4809 GradientStopKey {
4810 offset: stop.offset,
4811 color: stop.color.into(),
4812 }
4813 }).collect()
4814}
4815
4816pub struct SceneRecycler {
4820 pub tx: Sender<BuiltScene>,
4821 rx: Receiver<BuiltScene>,
4822
4823 pub prim_store: PrimitiveStore,
4826 pub clip_store: ClipStore,
4827 pub picture_graph: PictureGraph,
4828 pub prim_instances: Vec<PrimitiveInstance>,
4829 pub surfaces: Vec<SurfaceInfo>,
4830 pub hit_testing_scene: Option<HitTestingScene>,
4831 pub clip_tree_builder: Option<ClipTreeBuilder>,
4832 id_to_index_mapper_stack: Vec<NodeIdToIndexMapper>,
4841 sc_stack: Vec<FlattenedStackingContext>,
4842 containing_block_stack: Vec<SpatialNodeIndex>,
4843 raster_space_stack: Vec<RasterSpace>,
4844 pending_shadow_items: VecDeque<ShadowItem>,
4845 iframe_size: Vec<LayoutSize>,
4846}
4847
4848impl SceneRecycler {
4849 pub fn new() -> Self {
4850 let (tx, rx) = unbounded_channel();
4851 SceneRecycler {
4852 tx,
4853 rx,
4854
4855 prim_instances: Vec::new(),
4856 surfaces: Vec::new(),
4857 prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()),
4858 clip_store: ClipStore::new(),
4859 picture_graph: PictureGraph::new(),
4860 hit_testing_scene: None,
4861 clip_tree_builder: None,
4862
4863 id_to_index_mapper_stack: Vec::new(),
4864 sc_stack: Vec::new(),
4865 containing_block_stack: Vec::new(),
4866 raster_space_stack: Vec::new(),
4867 pending_shadow_items: VecDeque::new(),
4868 iframe_size: Vec::new(),
4869 }
4870 }
4871
4872 #[inline(never)]
4878 pub fn recycle_built_scene(&mut self) {
4879 let Ok(scene) = self.rx.try_recv() else {
4880 return;
4881 };
4882
4883 self.prim_store = scene.prim_store;
4884 self.clip_store = scene.clip_store;
4885 self.prim_store.reset();
4889 self.clip_store.reset();
4890 self.hit_testing_scene = Arc::try_unwrap(scene.hit_testing_scene).ok();
4891 self.picture_graph = scene.picture_graph;
4892 self.prim_instances = scene.prim_instances;
4893 self.surfaces = scene.surfaces;
4894 if let Some(clip_tree_builder) = &mut self.clip_tree_builder {
4895 clip_tree_builder.recycle_tree(scene.clip_tree);
4896 }
4897
4898 while let Ok(_) = self.rx.try_recv() {
4899 }
4902
4903 }
4905}