1
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter};
7 use api::{ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect};
8 use api::{DeviceIntSize, DevicePixelScale, DeviceUintRect, DeviceUintSize};
9 use api::{DisplayItemRef, Epoch, ExtendMode, ExternalScrollId, FilterOp};
10 use api::{FontInstanceKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
11 use api::{IframeDisplayItem, ImageDisplayItem, ImageKey, ImageRendering, ItemRange, LayerPoint};
12 use api::{LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D, LayoutSize, LayoutTransform};
13 use api::{LayoutVector2D, LineOrientation, LineStyle, LocalClip, PipelineId};
14 use api::{PropertyBinding, RepeatMode, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity};
15 use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
16 use api::{TileOffset, TransformStyle, YuvColorSpace, YuvData};
17 use app_units::Au;
18 use border::ImageBorderSegment;
19 use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
20 use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
21 use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
22 use euclid::{SideOffsets2D, rect, vec2};
23 use frame_builder::{FrameBuilder, FrameBuilderConfig};
24 use glyph_rasterizer::FontInstance;
25 use hit_test::{HitTestingItem, HitTestingRun};
26 use internal_types::{FastHashMap, FastHashSet};
27 use picture::{PictureCompositeMode, PictureKind, PicturePrimitive};
28 use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
29 use prim_store::{CachedGradientIndex, ImageCacheKey, ImagePrimitiveCpu, ImageSource};
30 use prim_store::{PrimitiveContainer, PrimitiveIndex, PrimitiveKind, PrimitiveStore};
31 use prim_store::{ScrollNodeAndClipChain, TextRunPrimitiveCpu};
32 use render_backend::{DocumentView};
33 use resource_cache::{FontInstanceMap, ImageRequest, TiledImageMap};
34 use scene::{Scene, ScenePipeline, StackingContextHelpers};
35 use scene_builder::{BuiltScene, SceneRequest};
36 use std::{f32, mem, usize};
37 use tiling::{CompositeOps, ScrollbarPrimitive};
38 use util::{MaxRect, RectHelpers, recycle_vec};
39
40 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
41 r: 0.3,
42 g: 0.3,
43 b: 0.3,
44 a: 0.6,
45 };
46
47 /// A data structure that keeps track of mapping between API ClipIds and the indices used
48 /// internally in the ClipScrollTree to avoid having to do HashMap lookups. ClipIdToIndexMapper is
49 /// responsible for mapping both ClipId to ClipChainIndex and ClipId to ClipScrollNodeIndex. We
50 /// also include two small LRU caches. Currently the caches are small (1 entry), but in the future
51 /// we could use uluru here to do something more involved.
52 #[derive(Default)]
53 pub struct ClipIdToIndexMapper {
54 /// A map which converts a ClipId for a clipping node or an API-defined ClipChain into
55 /// a ClipChainIndex, which is the index used internally in the ClipScrollTree to
56 /// identify ClipChains.
57 clip_chain_map: FastHashMap<ClipId, ClipChainIndex>,
58
59 /// The last mapped ClipChainIndex, used to avoid having to do lots of consecutive
60 /// HashMap lookups.
61 cached_clip_chain_index: Option<(ClipId, ClipChainIndex)>,
62
63 /// The offset in the ClipScrollTree's array of ClipScrollNodes for a particular pipeline.
64 /// This is used to convert a ClipId into a ClipScrollNodeIndex.
65 pipeline_offsets: FastHashMap<PipelineId, usize>,
66
67 /// The last mapped pipeline offset for this mapper. This is used to avoid having to
68 /// consult `pipeline_offsets` repeatedly when flattening the display list.
69 cached_pipeline_offset: Option<(PipelineId, usize)>,
70
71 /// The next available pipeline offset for ClipScrollNodeIndex. When we encounter a pipeline
72 /// we will use this value and increment it by the total number of ClipScrollNodes in the
73 /// pipeline's display list.
74 next_available_offset: usize,
75 }
76
77 impl ClipIdToIndexMapper {
add_clip_chain(&mut self, id: ClipId, index: ClipChainIndex)78 pub fn add_clip_chain(&mut self, id: ClipId, index: ClipChainIndex) {
79 debug_assert!(!self.clip_chain_map.contains_key(&id));
80 self.clip_chain_map.insert(id, index);
81 }
82
map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId)83 pub fn map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId) {
84 let parent_chain_index = self.get_clip_chain_index(parent_id);
85 self.add_clip_chain(id, parent_chain_index);
86 }
87
get_clip_chain_index(&mut self, id: &ClipId) -> ClipChainIndex88 pub fn get_clip_chain_index(&mut self, id: &ClipId) -> ClipChainIndex {
89 match self.cached_clip_chain_index {
90 Some((cached_id, cached_clip_chain_index)) if cached_id == *id =>
91 return cached_clip_chain_index,
92 _ => {}
93 }
94
95 self.clip_chain_map[id]
96 }
97
get_clip_chain_index_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex98 pub fn get_clip_chain_index_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex {
99 let index = self.get_clip_chain_index(id);
100 self.cached_clip_chain_index = Some((*id, index));
101 index
102 }
103
map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain104 pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
105 ScrollNodeAndClipChain::new(
106 self.get_node_index(info.scroll_node_id),
107 self.get_clip_chain_index_and_cache_result(&info.clip_node_id())
108 )
109 }
110
simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain111 pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
112 self.map_clip_and_scroll(&ClipAndScrollInfo::simple(*id))
113 }
114
initialize_for_pipeline(&mut self, pipeline: &ScenePipeline)115 pub fn initialize_for_pipeline(&mut self, pipeline: &ScenePipeline) {
116 debug_assert!(!self.pipeline_offsets.contains_key(&pipeline.pipeline_id));
117 self.pipeline_offsets.insert(pipeline.pipeline_id, self.next_available_offset);
118 self.next_available_offset += pipeline.display_list.total_clip_ids();
119 }
120
get_node_index(&mut self, id: ClipId) -> ClipScrollNodeIndex121 pub fn get_node_index(&mut self, id: ClipId) -> ClipScrollNodeIndex {
122 let (index, pipeline_id) = match id {
123 ClipId::Clip(index, pipeline_id) => (index, pipeline_id),
124 ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
125 };
126
127 let pipeline_offset = match self.cached_pipeline_offset {
128 Some((last_used_id, offset)) if last_used_id == pipeline_id => offset,
129 _ => {
130 let offset = self.pipeline_offsets[&pipeline_id];
131 self.cached_pipeline_offset = Some((pipeline_id, offset));
132 offset
133 }
134 };
135
136 ClipScrollNodeIndex(pipeline_offset + index)
137 }
138 }
139
140 /// A structure that converts a serialized display list into a form that WebRender
141 /// can use to later build a frame. This structure produces a FrameBuilder. Public
142 /// members are typically those that are destructured into the FrameBuilder.
143 pub struct DisplayListFlattener<'a> {
144 /// The scene that we are currently flattening.
145 scene: &'a Scene,
146
147 /// The ClipScrollTree that we are currently building during flattening.
148 clip_scroll_tree: &'a mut ClipScrollTree,
149
150 /// The map of all font instances.
151 font_instances: FontInstanceMap,
152
153 /// The map of tiled images.
154 tiled_image_map: TiledImageMap,
155
156 /// Used to track the latest flattened epoch for each pipeline.
157 pipeline_epochs: Vec<(PipelineId, Epoch)>,
158
159 /// A set of pipelines that the caller has requested be made available as
160 /// output textures.
161 output_pipelines: &'a FastHashSet<PipelineId>,
162
163 /// A list of replacements to make in order to properly handle fixed position
164 /// content as well as stacking contexts that create reference frames.
165 replacements: Vec<(ClipId, ClipId)>,
166
167 /// The data structure that converting between ClipId and the various index
168 /// types that the ClipScrollTree uses.
169 id_to_index_mapper: ClipIdToIndexMapper,
170
171 /// A stack of the current shadow primitives. The sub-Vec stores
172 /// a buffer of fast-path primitives to be appended on pop.
173 shadow_prim_stack: Vec<(PrimitiveIndex, Vec<(PrimitiveIndex, ScrollNodeAndClipChain)>)>,
174
175 /// A buffer of "real" content when doing fast-path shadows. This is appended
176 /// when the shadow stack is empty.
177 pending_shadow_contents: Vec<(PrimitiveIndex, ScrollNodeAndClipChain, LayerPrimitiveInfo)>,
178
179 /// A stack of scroll nodes used during display list processing to properly
180 /// parent new scroll nodes.
181 reference_frame_stack: Vec<(ClipId, ClipScrollNodeIndex)>,
182
183 /// A stack of stacking context properties.
184 sc_stack: Vec<FlattenedStackingContext>,
185
186 /// A stack of the current pictures.
187 picture_stack: Vec<PrimitiveIndex>,
188
189 /// A list of scrollbar primitives.
190 pub scrollbar_prims: Vec<ScrollbarPrimitive>,
191
192 /// The store of primitives.
193 pub prim_store: PrimitiveStore,
194
195 /// Information about all primitives involved in hit testing.
196 pub hit_testing_runs: Vec<HitTestingRun>,
197
198 /// The store which holds all complex clipping information.
199 pub clip_store: ClipStore,
200
201 /// The configuration to use for the FrameBuilder. We consult this in
202 /// order to determine the default font.
203 pub config: FrameBuilderConfig,
204
205 /// The gradients collecting during display list flattening.
206 pub cached_gradients: Vec<CachedGradient>,
207 }
208
209 impl<'a> DisplayListFlattener<'a> {
create_frame_builder( old_builder: FrameBuilder, scene: &Scene, clip_scroll_tree: &mut ClipScrollTree, font_instances: FontInstanceMap, tiled_image_map: TiledImageMap, view: &DocumentView, output_pipelines: &FastHashSet<PipelineId>, frame_builder_config: &FrameBuilderConfig, new_scene: &mut Scene, ) -> FrameBuilder210 pub fn create_frame_builder(
211 old_builder: FrameBuilder,
212 scene: &Scene,
213 clip_scroll_tree: &mut ClipScrollTree,
214 font_instances: FontInstanceMap,
215 tiled_image_map: TiledImageMap,
216 view: &DocumentView,
217 output_pipelines: &FastHashSet<PipelineId>,
218 frame_builder_config: &FrameBuilderConfig,
219 new_scene: &mut Scene,
220 ) -> FrameBuilder {
221 // We checked that the root pipeline is available on the render backend.
222 let root_pipeline_id = scene.root_pipeline_id.unwrap();
223 let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
224
225 let root_epoch = scene.pipeline_epochs[&root_pipeline_id];
226
227 let background_color = root_pipeline
228 .background_color
229 .and_then(|color| if color.a > 0.0 { Some(color) } else { None });
230
231 let mut flattener = DisplayListFlattener {
232 scene,
233 clip_scroll_tree,
234 font_instances,
235 tiled_image_map,
236 config: *frame_builder_config,
237 pipeline_epochs: Vec::new(),
238 replacements: Vec::new(),
239 output_pipelines,
240 id_to_index_mapper: ClipIdToIndexMapper::default(),
241 hit_testing_runs: recycle_vec(old_builder.hit_testing_runs),
242 shadow_prim_stack: Vec::new(),
243 cached_gradients: recycle_vec(old_builder.cached_gradients),
244 pending_shadow_contents: Vec::new(),
245 scrollbar_prims: recycle_vec(old_builder.scrollbar_prims),
246 reference_frame_stack: Vec::new(),
247 picture_stack: Vec::new(),
248 sc_stack: Vec::new(),
249 prim_store: old_builder.prim_store.recycle(),
250 clip_store: old_builder.clip_store.recycle(),
251 };
252
253 flattener.id_to_index_mapper.initialize_for_pipeline(root_pipeline);
254 flattener.push_root(
255 root_pipeline_id,
256 &root_pipeline.viewport_size,
257 &root_pipeline.content_size,
258 );
259 flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
260 flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
261
262 debug_assert!(flattener.picture_stack.is_empty());
263
264 new_scene.root_pipeline_id = Some(root_pipeline_id);
265 new_scene.pipeline_epochs.insert(root_pipeline_id, root_epoch);
266 new_scene.pipeline_epochs.extend(flattener.pipeline_epochs.drain(..));
267 new_scene.pipelines = scene.pipelines.clone();
268
269 FrameBuilder::with_display_list_flattener(
270 view.inner_rect,
271 background_color,
272 view.window_size,
273 flattener
274 )
275 }
276
277 /// Since WebRender still handles fixed position and reference frame content internally
278 /// we need to apply this table of id replacements only to the id that affects the
279 /// position of a node. We can eventually remove this when clients start handling
280 /// reference frames themselves. This method applies these replacements.
apply_scroll_frame_id_replacement(&self, index: ClipId) -> ClipId281 fn apply_scroll_frame_id_replacement(&self, index: ClipId) -> ClipId {
282 match self.replacements.last() {
283 Some(&(to_replace, replacement)) if to_replace == index => replacement,
284 _ => index,
285 }
286 }
287
get_complex_clips( &self, pipeline_id: PipelineId, complex_clips: ItemRange<ComplexClipRegion>, ) -> Vec<ComplexClipRegion>288 fn get_complex_clips(
289 &self,
290 pipeline_id: PipelineId,
291 complex_clips: ItemRange<ComplexClipRegion>,
292 ) -> Vec<ComplexClipRegion> {
293 if complex_clips.is_empty() {
294 return vec![];
295 }
296
297 self.scene
298 .pipelines
299 .get(&pipeline_id)
300 .expect("No display list?")
301 .display_list
302 .get(complex_clips)
303 .collect()
304 }
305
get_clip_chain_items( &self, pipeline_id: PipelineId, items: ItemRange<ClipId>, ) -> Vec<ClipId>306 fn get_clip_chain_items(
307 &self,
308 pipeline_id: PipelineId,
309 items: ItemRange<ClipId>,
310 ) -> Vec<ClipId> {
311 if items.is_empty() {
312 return vec![];
313 }
314
315 self.scene
316 .pipelines
317 .get(&pipeline_id)
318 .expect("No display list?")
319 .display_list
320 .get(items)
321 .collect()
322 }
323
flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize)324 fn flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize) {
325 let pipeline_id = pipeline.pipeline_id;
326 let reference_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
327 &ClipId::root_reference_frame(pipeline_id)
328 );
329 let scroll_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
330 &ClipId::root_scroll_node(pipeline_id)
331 );
332
333 self.push_stacking_context(
334 pipeline_id,
335 CompositeOps::default(),
336 TransformStyle::Flat,
337 true,
338 true,
339 scroll_frame_info,
340 );
341
342 // For the root pipeline, there's no need to add a full screen rectangle
343 // here, as it's handled by the framebuffer clear.
344 if self.scene.root_pipeline_id != Some(pipeline_id) {
345 if let Some(pipeline) = self.scene.pipelines.get(&pipeline_id) {
346 if let Some(bg_color) = pipeline.background_color {
347 let root_bounds = LayerRect::new(LayerPoint::zero(), *frame_size);
348 let info = LayerPrimitiveInfo::new(root_bounds);
349 self.add_solid_rectangle(
350 reference_frame_info,
351 &info,
352 bg_color,
353 None,
354 );
355 }
356 }
357 }
358
359 self.flatten_items(&mut pipeline.display_list.iter(), pipeline_id, LayerVector2D::zero());
360
361 if self.config.enable_scrollbars {
362 let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
363 let container_rect = LayerRect::new(LayerPoint::zero(), *frame_size);
364 self.add_scroll_bar(
365 reference_frame_info,
366 &LayerPrimitiveInfo::new(scrollbar_rect),
367 DEFAULT_SCROLLBAR_COLOR,
368 ScrollbarInfo(scroll_frame_info.scroll_node_id, container_rect),
369 );
370 }
371
372 self.pop_stacking_context();
373 }
374
flatten_items( &mut self, traversal: &mut BuiltDisplayListIter<'a>, pipeline_id: PipelineId, reference_frame_relative_offset: LayerVector2D, )375 fn flatten_items(
376 &mut self,
377 traversal: &mut BuiltDisplayListIter<'a>,
378 pipeline_id: PipelineId,
379 reference_frame_relative_offset: LayerVector2D,
380 ) {
381 loop {
382 let subtraversal = {
383 let item = match traversal.next() {
384 Some(item) => item,
385 None => break,
386 };
387
388 if SpecificDisplayItem::PopStackingContext == *item.item() {
389 return;
390 }
391
392 self.flatten_item(
393 item,
394 pipeline_id,
395 reference_frame_relative_offset,
396 )
397 };
398
399 // If flatten_item created a sub-traversal, we need `traversal` to have the
400 // same state as the completed subtraversal, so we reinitialize it here.
401 if let Some(subtraversal) = subtraversal {
402 *traversal = subtraversal;
403 }
404 }
405 }
406
flatten_sticky_frame( &mut self, item: &DisplayItemRef, info: &StickyFrameDisplayItem, clip_and_scroll: &ScrollNodeAndClipChain, parent_id: &ClipId, reference_frame_relative_offset: &LayerVector2D, )407 fn flatten_sticky_frame(
408 &mut self,
409 item: &DisplayItemRef,
410 info: &StickyFrameDisplayItem,
411 clip_and_scroll: &ScrollNodeAndClipChain,
412 parent_id: &ClipId,
413 reference_frame_relative_offset: &LayerVector2D,
414 ) {
415 let frame_rect = item.rect().translate(&reference_frame_relative_offset);
416 let sticky_frame_info = StickyFrameInfo::new(
417 info.margins,
418 info.vertical_offset_bounds,
419 info.horizontal_offset_bounds,
420 info.previously_applied_offset,
421 );
422
423 let index = self.id_to_index_mapper.get_node_index(info.id);
424 self.clip_scroll_tree.add_sticky_frame(
425 index,
426 clip_and_scroll.scroll_node_id, /* parent id */
427 frame_rect,
428 sticky_frame_info,
429 info.id.pipeline_id(),
430 );
431 self.id_to_index_mapper.map_to_parent_clip_chain(info.id, &parent_id);
432 }
433
flatten_scroll_frame( &mut self, item: &DisplayItemRef, info: &ScrollFrameDisplayItem, pipeline_id: PipelineId, clip_and_scroll_ids: &ClipAndScrollInfo, reference_frame_relative_offset: &LayerVector2D, )434 fn flatten_scroll_frame(
435 &mut self,
436 item: &DisplayItemRef,
437 info: &ScrollFrameDisplayItem,
438 pipeline_id: PipelineId,
439 clip_and_scroll_ids: &ClipAndScrollInfo,
440 reference_frame_relative_offset: &LayerVector2D,
441 ) {
442 let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
443 let clip_region = ClipRegion::create_for_clip_node(
444 *item.local_clip().clip_rect(),
445 complex_clips,
446 info.image_mask,
447 &reference_frame_relative_offset,
448 );
449 // Just use clip rectangle as the frame rect for this scroll frame.
450 // This is useful when calculating scroll extents for the
451 // ClipScrollNode::scroll(..) API as well as for properly setting sticky
452 // positioning offsets.
453 let frame_rect = item.local_clip()
454 .clip_rect()
455 .translate(&reference_frame_relative_offset);
456 let content_rect = item.rect().translate(&reference_frame_relative_offset);
457
458 debug_assert!(info.clip_id != info.scroll_frame_id);
459
460 self.add_clip_node(info.clip_id, clip_and_scroll_ids.scroll_node_id, clip_region);
461
462 self.add_scroll_frame(
463 info.scroll_frame_id,
464 info.clip_id,
465 info.external_id,
466 pipeline_id,
467 &frame_rect,
468 &content_rect.size,
469 info.scroll_sensitivity,
470 );
471 }
472
flatten_stacking_context( &mut self, traversal: &mut BuiltDisplayListIter<'a>, pipeline_id: PipelineId, unreplaced_scroll_id: ClipId, mut scroll_node_id: ClipId, mut reference_frame_relative_offset: LayerVector2D, bounds: &LayerRect, stacking_context: &StackingContext, filters: ItemRange<FilterOp>, is_backface_visible: bool, )473 fn flatten_stacking_context(
474 &mut self,
475 traversal: &mut BuiltDisplayListIter<'a>,
476 pipeline_id: PipelineId,
477 unreplaced_scroll_id: ClipId,
478 mut scroll_node_id: ClipId,
479 mut reference_frame_relative_offset: LayerVector2D,
480 bounds: &LayerRect,
481 stacking_context: &StackingContext,
482 filters: ItemRange<FilterOp>,
483 is_backface_visible: bool,
484 ) {
485 // Avoid doing unnecessary work for empty stacking contexts.
486 if traversal.current_stacking_context_empty() {
487 traversal.skip_current_stacking_context();
488 return;
489 }
490
491 let composition_operations = {
492 // TODO(optimization?): self.traversal.display_list()
493 let display_list = &self
494 .scene
495 .pipelines
496 .get(&pipeline_id)
497 .expect("No display list?!")
498 .display_list;
499 CompositeOps::new(
500 stacking_context.filter_ops_for_compositing(display_list, filters),
501 stacking_context.mix_blend_mode_for_compositing(),
502 )
503 };
504
505 if stacking_context.scroll_policy == ScrollPolicy::Fixed {
506 scroll_node_id = self.current_reference_frame_id();
507 self.replacements.push((unreplaced_scroll_id, scroll_node_id));
508 }
509
510 reference_frame_relative_offset += bounds.origin.to_vector();
511
512 // If we have a transformation or a perspective, we should have been assigned a new
513 // reference frame id. This means this stacking context establishes a new reference frame.
514 // Descendant fixed position content will be positioned relative to us.
515 if let Some(reference_frame_id) = stacking_context.reference_frame_id {
516 debug_assert!(
517 stacking_context.transform.is_some() ||
518 stacking_context.perspective.is_some()
519 );
520
521 let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
522 self.push_reference_frame(
523 reference_frame_id,
524 Some(scroll_node_id),
525 pipeline_id,
526 &reference_frame_bounds,
527 stacking_context.transform,
528 stacking_context.perspective,
529 reference_frame_relative_offset,
530 );
531 self.replacements.push((unreplaced_scroll_id, reference_frame_id));
532 reference_frame_relative_offset = LayerVector2D::zero();
533 }
534
535 // We apply the replacements one more time in case we need to set it to a replacement
536 // that we just pushed above.
537 let sc_scroll_node = self.apply_scroll_frame_id_replacement(unreplaced_scroll_id);
538 let stacking_context_clip_and_scroll =
539 self.id_to_index_mapper.simple_scroll_and_clip_chain(&sc_scroll_node);
540 self.push_stacking_context(
541 pipeline_id,
542 composition_operations,
543 stacking_context.transform_style,
544 is_backface_visible,
545 false,
546 stacking_context_clip_and_scroll,
547 );
548
549 self.flatten_items(
550 traversal,
551 pipeline_id,
552 reference_frame_relative_offset,
553 );
554
555 if stacking_context.scroll_policy == ScrollPolicy::Fixed {
556 self.replacements.pop();
557 }
558
559 if stacking_context.reference_frame_id.is_some() {
560 self.replacements.pop();
561 self.pop_reference_frame();
562 }
563
564 self.pop_stacking_context();
565 }
566
flatten_iframe( &mut self, item: &DisplayItemRef, info: &IframeDisplayItem, clip_and_scroll_ids: &ClipAndScrollInfo, reference_frame_relative_offset: &LayerVector2D, )567 fn flatten_iframe(
568 &mut self,
569 item: &DisplayItemRef,
570 info: &IframeDisplayItem,
571 clip_and_scroll_ids: &ClipAndScrollInfo,
572 reference_frame_relative_offset: &LayerVector2D,
573 ) {
574 let iframe_pipeline_id = info.pipeline_id;
575 let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
576 Some(pipeline) => pipeline,
577 None => return,
578 };
579
580 self.id_to_index_mapper.initialize_for_pipeline(pipeline);
581
582 self.add_clip_node(
583 info.clip_id,
584 clip_and_scroll_ids.scroll_node_id,
585 ClipRegion::create_for_clip_node_with_local_clip(
586 &item.local_clip(),
587 &reference_frame_relative_offset
588 ),
589 );
590
591 let epoch = self.scene.pipeline_epochs[&iframe_pipeline_id];
592 self.pipeline_epochs.push((iframe_pipeline_id, epoch));
593
594 let bounds = item.rect();
595 let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
596 let origin = *reference_frame_relative_offset + bounds.origin.to_vector();
597 self.push_reference_frame(
598 ClipId::root_reference_frame(iframe_pipeline_id),
599 Some(info.clip_id),
600 iframe_pipeline_id,
601 &iframe_rect,
602 None,
603 None,
604 origin,
605 );
606
607 self.add_scroll_frame(
608 ClipId::root_scroll_node(iframe_pipeline_id),
609 ClipId::root_reference_frame(iframe_pipeline_id),
610 Some(ExternalScrollId(0, iframe_pipeline_id)),
611 iframe_pipeline_id,
612 &iframe_rect,
613 &pipeline.content_size,
614 ScrollSensitivity::ScriptAndInputEvents,
615 );
616
617 self.flatten_root(pipeline, &iframe_rect.size);
618
619 self.pop_reference_frame();
620 }
621
flatten_item<'b>( &'b mut self, item: DisplayItemRef<'a, 'b>, pipeline_id: PipelineId, reference_frame_relative_offset: LayerVector2D, ) -> Option<BuiltDisplayListIter<'a>>622 fn flatten_item<'b>(
623 &'b mut self,
624 item: DisplayItemRef<'a, 'b>,
625 pipeline_id: PipelineId,
626 reference_frame_relative_offset: LayerVector2D,
627 ) -> Option<BuiltDisplayListIter<'a>> {
628 let mut clip_and_scroll_ids = item.clip_and_scroll();
629 let unreplaced_scroll_id = clip_and_scroll_ids.scroll_node_id;
630 clip_and_scroll_ids.scroll_node_id =
631 self.apply_scroll_frame_id_replacement(clip_and_scroll_ids.scroll_node_id);
632 let clip_and_scroll = self.id_to_index_mapper.map_clip_and_scroll(&clip_and_scroll_ids);
633
634 let prim_info = item.get_layer_primitive_info(&reference_frame_relative_offset);
635 match *item.item() {
636 SpecificDisplayItem::Image(ref info) => {
637 match self.tiled_image_map.get(&info.image_key).cloned() {
638 Some(tiling) => {
639 // The image resource is tiled. We have to generate an image primitive
640 // for each tile.
641 self.decompose_image(
642 clip_and_scroll,
643 &prim_info,
644 info,
645 tiling.image_size,
646 tiling.tile_size as u32,
647 );
648 }
649 None => {
650 self.add_image(
651 clip_and_scroll,
652 &prim_info,
653 info.stretch_size,
654 info.tile_spacing,
655 None,
656 info.image_key,
657 info.image_rendering,
658 info.alpha_type,
659 None,
660 );
661 }
662 }
663 }
664 SpecificDisplayItem::YuvImage(ref info) => {
665 self.add_yuv_image(
666 clip_and_scroll,
667 &prim_info,
668 info.yuv_data,
669 info.color_space,
670 info.image_rendering,
671 );
672 }
673 SpecificDisplayItem::Text(ref text_info) => {
674 self.add_text(
675 clip_and_scroll,
676 reference_frame_relative_offset,
677 &prim_info,
678 &text_info.font_key,
679 &text_info.color,
680 item.glyphs(),
681 item.display_list().get(item.glyphs()).count(),
682 text_info.glyph_options,
683 );
684 }
685 SpecificDisplayItem::Rectangle(ref info) => {
686 self.add_solid_rectangle(
687 clip_and_scroll,
688 &prim_info,
689 info.color,
690 None,
691 );
692 }
693 SpecificDisplayItem::ClearRectangle => {
694 self.add_clear_rectangle(
695 clip_and_scroll,
696 &prim_info,
697 );
698 }
699 SpecificDisplayItem::Line(ref info) => {
700 self.add_line(
701 clip_and_scroll,
702 &prim_info,
703 info.wavy_line_thickness,
704 info.orientation,
705 &info.color,
706 info.style,
707 );
708 }
709 SpecificDisplayItem::Gradient(ref info) => {
710 self.add_gradient(
711 clip_and_scroll,
712 &prim_info,
713 info.gradient.start_point,
714 info.gradient.end_point,
715 item.gradient_stops(),
716 item.display_list().get(item.gradient_stops()).count(),
717 info.gradient.extend_mode,
718 info.tile_size,
719 info.tile_spacing,
720 );
721 }
722 SpecificDisplayItem::RadialGradient(ref info) => {
723 self.add_radial_gradient(
724 clip_and_scroll,
725 &prim_info,
726 info.gradient.start_center,
727 info.gradient.start_radius,
728 info.gradient.end_center,
729 info.gradient.end_radius,
730 info.gradient.ratio_xy,
731 item.gradient_stops(),
732 info.gradient.extend_mode,
733 info.tile_size,
734 info.tile_spacing,
735 );
736 }
737 SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
738 let bounds = box_shadow_info
739 .box_bounds
740 .translate(&reference_frame_relative_offset);
741 let mut prim_info = prim_info.clone();
742 prim_info.rect = bounds;
743 self.add_box_shadow(
744 clip_and_scroll,
745 &prim_info,
746 &box_shadow_info.offset,
747 &box_shadow_info.color,
748 box_shadow_info.blur_radius,
749 box_shadow_info.spread_radius,
750 box_shadow_info.border_radius,
751 box_shadow_info.clip_mode,
752 );
753 }
754 SpecificDisplayItem::Border(ref info) => {
755 self.add_border(
756 clip_and_scroll,
757 &prim_info,
758 info,
759 item.gradient_stops(),
760 item.display_list().get(item.gradient_stops()).count(),
761 );
762 }
763 SpecificDisplayItem::PushStackingContext(ref info) => {
764 let mut subtraversal = item.sub_iter();
765 self.flatten_stacking_context(
766 &mut subtraversal,
767 pipeline_id,
768 unreplaced_scroll_id,
769 clip_and_scroll_ids.scroll_node_id,
770 reference_frame_relative_offset,
771 &item.rect(),
772 &info.stacking_context,
773 item.filters(),
774 prim_info.is_backface_visible,
775 );
776 return Some(subtraversal);
777 }
778 SpecificDisplayItem::Iframe(ref info) => {
779 self.flatten_iframe(
780 &item,
781 info,
782 &clip_and_scroll_ids,
783 &reference_frame_relative_offset
784 );
785 }
786 SpecificDisplayItem::Clip(ref info) => {
787 let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
788 let clip_region = ClipRegion::create_for_clip_node(
789 *item.local_clip().clip_rect(),
790 complex_clips,
791 info.image_mask,
792 &reference_frame_relative_offset,
793 );
794 self.add_clip_node(info.id, clip_and_scroll_ids.scroll_node_id, clip_region);
795 }
796 SpecificDisplayItem::ClipChain(ref info) => {
797 let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items())
798 .iter()
799 .map(|id| self.id_to_index_mapper.get_node_index(*id))
800 .collect();
801 let parent = info.parent.map(|id|
802 self.id_to_index_mapper.get_clip_chain_index(&ClipId::ClipChain(id))
803 );
804 let clip_chain_index =
805 self.clip_scroll_tree.add_clip_chain_descriptor(parent, items);
806 self.id_to_index_mapper.add_clip_chain(ClipId::ClipChain(info.id), clip_chain_index);
807 },
808 SpecificDisplayItem::ScrollFrame(ref info) => {
809 self.flatten_scroll_frame(
810 &item,
811 info,
812 pipeline_id,
813 &clip_and_scroll_ids,
814 &reference_frame_relative_offset
815 );
816 }
817 SpecificDisplayItem::StickyFrame(ref info) => {
818 self.flatten_sticky_frame(
819 &item,
820 info,
821 &clip_and_scroll,
822 &clip_and_scroll_ids.scroll_node_id,
823 &reference_frame_relative_offset
824 );
825 }
826
827 // Do nothing; these are dummy items for the display list parser
828 SpecificDisplayItem::SetGradientStops => {}
829
830 SpecificDisplayItem::PopStackingContext => {
831 unreachable!("Should have returned in parent method.")
832 }
833 SpecificDisplayItem::PushShadow(shadow) => {
834 let mut prim_info = prim_info.clone();
835 prim_info.rect = LayerRect::zero();
836 self
837 .push_shadow(shadow, clip_and_scroll, &prim_info);
838 }
839 SpecificDisplayItem::PopAllShadows => {
840 self.pop_all_shadows();
841 }
842 }
843 None
844 }
845
846 /// Decomposes an image display item that is repeated into an image per individual repetition.
847 /// We need to do this when we are unable to perform the repetition in the shader,
848 /// for example if the image is tiled.
849 ///
850 /// In all of the "decompose" methods below, we independently handle horizontal and vertical
851 /// decomposition. This lets us generate the minimum amount of primitives by, for example,
852 /// decompositing the repetition horizontally while repeating vertically in the shader (for
853 /// an image where the width is too bug but the height is not).
854 ///
855 /// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
856 /// takes care of the decomposition required by the internal tiling of the image.
decompose_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, image_size: DeviceUintSize, tile_size: u32, )857 fn decompose_image(
858 &mut self,
859 clip_and_scroll: ScrollNodeAndClipChain,
860 prim_info: &LayerPrimitiveInfo,
861 info: &ImageDisplayItem,
862 image_size: DeviceUintSize,
863 tile_size: u32,
864 ) {
865 let no_vertical_tiling = image_size.height <= tile_size;
866 let no_vertical_spacing = info.tile_spacing.height == 0.0;
867 let item_rect = prim_info.rect;
868 if no_vertical_tiling && no_vertical_spacing {
869 self.decompose_image_row(
870 clip_and_scroll,
871 prim_info,
872 info,
873 image_size,
874 tile_size,
875 );
876 return;
877 }
878
879 // Decompose each vertical repetition into rows.
880 let layout_stride = info.stretch_size.height + info.tile_spacing.height;
881 let num_repetitions = (item_rect.size.height / layout_stride).ceil() as u32;
882 for i in 0 .. num_repetitions {
883 if let Some(row_rect) = rect(
884 item_rect.origin.x,
885 item_rect.origin.y + (i as f32) * layout_stride,
886 item_rect.size.width,
887 info.stretch_size.height,
888 ).intersection(&item_rect)
889 {
890 let mut prim_info = prim_info.clone();
891 prim_info.rect = row_rect;
892 self.decompose_image_row(
893 clip_and_scroll,
894 &prim_info,
895 info,
896 image_size,
897 tile_size,
898 );
899 }
900 }
901 }
902
decompose_image_row( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, image_size: DeviceUintSize, tile_size: u32, )903 fn decompose_image_row(
904 &mut self,
905 clip_and_scroll: ScrollNodeAndClipChain,
906 prim_info: &LayerPrimitiveInfo,
907 info: &ImageDisplayItem,
908 image_size: DeviceUintSize,
909 tile_size: u32,
910 ) {
911 let no_horizontal_tiling = image_size.width <= tile_size;
912 let no_horizontal_spacing = info.tile_spacing.width == 0.0;
913 if no_horizontal_tiling && no_horizontal_spacing {
914 self.decompose_tiled_image(
915 clip_and_scroll,
916 prim_info,
917 info,
918 image_size,
919 tile_size,
920 );
921 return;
922 }
923
924 // Decompose each horizontal repetition.
925 let item_rect = prim_info.rect;
926 let layout_stride = info.stretch_size.width + info.tile_spacing.width;
927 let num_repetitions = (item_rect.size.width / layout_stride).ceil() as u32;
928 for i in 0 .. num_repetitions {
929 if let Some(decomposed_rect) = rect(
930 item_rect.origin.x + (i as f32) * layout_stride,
931 item_rect.origin.y,
932 info.stretch_size.width,
933 item_rect.size.height,
934 ).intersection(&item_rect)
935 {
936 let mut prim_info = prim_info.clone();
937 prim_info.rect = decomposed_rect;
938 self.decompose_tiled_image(
939 clip_and_scroll,
940 &prim_info,
941 info,
942 image_size,
943 tile_size,
944 );
945 }
946 }
947 }
948
decompose_tiled_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, image_size: DeviceUintSize, tile_size: u32, )949 fn decompose_tiled_image(
950 &mut self,
951 clip_and_scroll: ScrollNodeAndClipChain,
952 prim_info: &LayerPrimitiveInfo,
953 info: &ImageDisplayItem,
954 image_size: DeviceUintSize,
955 tile_size: u32,
956 ) {
957 // The image resource is tiled. We have to generate an image primitive
958 // for each tile.
959 // We need to do this because the image is broken up into smaller tiles in the texture
960 // cache and the image shader is not able to work with this type of sparse representation.
961
962 // The tiling logic works as follows:
963 //
964 // ###################-+ -+
965 // # | | |//# | | image size
966 // # | | |//# | |
967 // #----+----+----+--#-+ | -+
968 // # | | |//# | | | regular tile size
969 // # | | |//# | | |
970 // #----+----+----+--#-+ | -+-+
971 // #////|////|////|//# | | | "leftover" height
972 // ################### | -+ ---+
973 // #----+----+----+----+
974 //
975 // In the ascii diagram above, a large image is plit into tiles of almost regular size.
976 // The tiles on the right and bottom edges (hatched in the diagram) are smaller than
977 // the regular tiles and are handled separately in the code see leftover_width/height.
978 // each generated image primitive corresponds to a tile in the texture cache, with the
979 // assumption that the smaller tiles with leftover sizes are sized to fit their own
980 // irregular size in the texture cache.
981 //
982 // For the case where we don't tile along an axis, we can still perform the repetition in
983 // the shader (for this particular axis), and it is worth special-casing for this to avoid
984 // generating many primitives.
985 // This can happen with very tall and thin images used as a repeating background.
986 // Apparently web authors do that...
987
988 let item_rect = prim_info.rect;
989 let needs_repeat_x = info.stretch_size.width < item_rect.size.width;
990 let needs_repeat_y = info.stretch_size.height < item_rect.size.height;
991
992 let tiled_in_x = image_size.width > tile_size;
993 let tiled_in_y = image_size.height > tile_size;
994
995 // If we don't actually tile in this dimension, repeating can be done in the shader.
996 let shader_repeat_x = needs_repeat_x && !tiled_in_x;
997 let shader_repeat_y = needs_repeat_y && !tiled_in_y;
998
999 let tile_size_f32 = tile_size as f32;
1000
1001 // Note: this rounds down so it excludes the partially filled tiles on the right and
1002 // bottom edges (we handle them separately below).
1003 let num_tiles_x = (image_size.width / tile_size) as u16;
1004 let num_tiles_y = (image_size.height / tile_size) as u16;
1005
1006 // Ratio between (image space) tile size and image size.
1007 let img_dw = tile_size_f32 / (image_size.width as f32);
1008 let img_dh = tile_size_f32 / (image_size.height as f32);
1009
1010 // Strected size of the tile in layout space.
1011 let stretched_tile_size = LayerSize::new(
1012 img_dw * info.stretch_size.width,
1013 img_dh * info.stretch_size.height,
1014 );
1015
1016 // The size in pixels of the tiles on the right and bottom edges, smaller
1017 // than the regular tile size if the image is not a multiple of the tile size.
1018 // Zero means the image size is a multiple of the tile size.
1019 let leftover =
1020 DeviceUintSize::new(image_size.width % tile_size, image_size.height % tile_size);
1021
1022 for ty in 0 .. num_tiles_y {
1023 for tx in 0 .. num_tiles_x {
1024 self.add_tile_primitive(
1025 clip_and_scroll,
1026 prim_info,
1027 info,
1028 TileOffset::new(tx, ty),
1029 stretched_tile_size,
1030 1.0,
1031 1.0,
1032 shader_repeat_x,
1033 shader_repeat_y,
1034 );
1035 }
1036 if leftover.width != 0 {
1037 // Tiles on the right edge that are smaller than the tile size.
1038 self.add_tile_primitive(
1039 clip_and_scroll,
1040 prim_info,
1041 info,
1042 TileOffset::new(num_tiles_x, ty),
1043 stretched_tile_size,
1044 (leftover.width as f32) / tile_size_f32,
1045 1.0,
1046 shader_repeat_x,
1047 shader_repeat_y,
1048 );
1049 }
1050 }
1051
1052 if leftover.height != 0 {
1053 for tx in 0 .. num_tiles_x {
1054 // Tiles on the bottom edge that are smaller than the tile size.
1055 self.add_tile_primitive(
1056 clip_and_scroll,
1057 prim_info,
1058 info,
1059 TileOffset::new(tx, num_tiles_y),
1060 stretched_tile_size,
1061 1.0,
1062 (leftover.height as f32) / tile_size_f32,
1063 shader_repeat_x,
1064 shader_repeat_y,
1065 );
1066 }
1067
1068 if leftover.width != 0 {
1069 // Finally, the bottom-right tile with a "leftover" size.
1070 self.add_tile_primitive(
1071 clip_and_scroll,
1072 prim_info,
1073 info,
1074 TileOffset::new(num_tiles_x, num_tiles_y),
1075 stretched_tile_size,
1076 (leftover.width as f32) / tile_size_f32,
1077 (leftover.height as f32) / tile_size_f32,
1078 shader_repeat_x,
1079 shader_repeat_y,
1080 );
1081 }
1082 }
1083 }
1084
add_tile_primitive( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, tile_offset: TileOffset, stretched_tile_size: LayerSize, tile_ratio_width: f32, tile_ratio_height: f32, shader_repeat_x: bool, shader_repeat_y: bool, )1085 fn add_tile_primitive(
1086 &mut self,
1087 clip_and_scroll: ScrollNodeAndClipChain,
1088 prim_info: &LayerPrimitiveInfo,
1089 info: &ImageDisplayItem,
1090 tile_offset: TileOffset,
1091 stretched_tile_size: LayerSize,
1092 tile_ratio_width: f32,
1093 tile_ratio_height: f32,
1094 shader_repeat_x: bool,
1095 shader_repeat_y: bool,
1096 ) {
1097 // If the the image is tiled along a given axis, we can't have the shader compute
1098 // the image repetition pattern. In this case we base the primitive's rectangle size
1099 // on the stretched tile size which effectively cancels the repetion (and repetition
1100 // has to be emulated by generating more primitives).
1101 // If the image is not tiled along this axis, we can perform the repetition in the
1102 // shader. in this case we use the item's size in the primitive (on that particular
1103 // axis).
1104 // See the shader_repeat_x/y code below.
1105
1106 let stretched_size = LayerSize::new(
1107 stretched_tile_size.width * tile_ratio_width,
1108 stretched_tile_size.height * tile_ratio_height,
1109 );
1110
1111 let mut prim_rect = LayerRect::new(
1112 prim_info.rect.origin +
1113 LayerVector2D::new(
1114 tile_offset.x as f32 * stretched_tile_size.width,
1115 tile_offset.y as f32 * stretched_tile_size.height,
1116 ),
1117 stretched_size,
1118 );
1119
1120 if shader_repeat_x {
1121 assert_eq!(tile_offset.x, 0);
1122 prim_rect.size.width = prim_info.rect.size.width;
1123 }
1124
1125 if shader_repeat_y {
1126 assert_eq!(tile_offset.y, 0);
1127 prim_rect.size.height = prim_info.rect.size.height;
1128 }
1129
1130 // Fix up the primitive's rect if it overflows the original item rect.
1131 if let Some(prim_rect) = prim_rect.intersection(&prim_info.rect) {
1132 let mut prim_info = prim_info.clone();
1133 prim_info.rect = prim_rect;
1134 self.add_image(
1135 clip_and_scroll,
1136 &prim_info,
1137 stretched_size,
1138 info.tile_spacing,
1139 None,
1140 info.image_key,
1141 info.image_rendering,
1142 info.alpha_type,
1143 Some(tile_offset),
1144 );
1145 }
1146 }
1147
1148 /// Create a primitive and add it to the prim store. This method doesn't
1149 /// add the primitive to the draw list, so can be used for creating
1150 /// sub-primitives.
create_primitive( &mut self, info: &LayerPrimitiveInfo, mut clip_sources: Vec<ClipSource>, container: PrimitiveContainer, ) -> PrimitiveIndex1151 pub fn create_primitive(
1152 &mut self,
1153 info: &LayerPrimitiveInfo,
1154 mut clip_sources: Vec<ClipSource>,
1155 container: PrimitiveContainer,
1156 ) -> PrimitiveIndex {
1157 if let &LocalClip::RoundedRect(main, region) = &info.local_clip {
1158 clip_sources.push(ClipSource::Rectangle(main));
1159
1160 clip_sources.push(ClipSource::new_rounded_rect(
1161 region.rect,
1162 region.radii,
1163 region.mode,
1164 ));
1165 }
1166
1167 let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
1168
1169 let clip_sources = if clip_sources.is_empty() {
1170 None
1171 } else {
1172 Some(self.clip_store.insert(ClipSources::new(clip_sources)))
1173 };
1174
1175 let prim_index = self.prim_store.add_primitive(
1176 &info.rect,
1177 &info.local_clip.clip_rect(),
1178 info.is_backface_visible && stacking_context.is_backface_visible,
1179 clip_sources,
1180 info.tag,
1181 container,
1182 );
1183
1184 prim_index
1185 }
1186
add_primitive_to_hit_testing_list( &mut self, info: &LayerPrimitiveInfo, clip_and_scroll: ScrollNodeAndClipChain )1187 pub fn add_primitive_to_hit_testing_list(
1188 &mut self,
1189 info: &LayerPrimitiveInfo,
1190 clip_and_scroll: ScrollNodeAndClipChain
1191 ) {
1192 let tag = match info.tag {
1193 Some(tag) => tag,
1194 None => return,
1195 };
1196
1197 let new_item = HitTestingItem::new(tag, info);
1198 match self.hit_testing_runs.last_mut() {
1199 Some(&mut HitTestingRun(ref mut items, prev_clip_and_scroll))
1200 if prev_clip_and_scroll == clip_and_scroll => {
1201 items.push(new_item);
1202 return;
1203 }
1204 _ => {}
1205 }
1206
1207 self.hit_testing_runs.push(HitTestingRun(vec![new_item], clip_and_scroll));
1208 }
1209
1210 /// Add an already created primitive to the draw lists.
add_primitive_to_draw_list( &mut self, prim_index: PrimitiveIndex, clip_and_scroll: ScrollNodeAndClipChain, )1211 pub fn add_primitive_to_draw_list(
1212 &mut self,
1213 prim_index: PrimitiveIndex,
1214 clip_and_scroll: ScrollNodeAndClipChain,
1215 ) {
1216 // Add primitive to the top-most Picture on the stack.
1217 // TODO(gw): Let's consider removing the extra indirection
1218 // needed to get a specific primitive index...
1219 let pic_prim_index = self.picture_stack.last().unwrap();
1220 let metadata = &self.prim_store.cpu_metadata[pic_prim_index.0];
1221 let pic = &mut self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
1222 pic.add_primitive(
1223 prim_index,
1224 clip_and_scroll
1225 );
1226 }
1227
1228 /// Convenience interface that creates a primitive entry and adds it
1229 /// to the draw list.
add_primitive( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, clip_sources: Vec<ClipSource>, container: PrimitiveContainer, ) -> PrimitiveIndex1230 pub fn add_primitive(
1231 &mut self,
1232 clip_and_scroll: ScrollNodeAndClipChain,
1233 info: &LayerPrimitiveInfo,
1234 clip_sources: Vec<ClipSource>,
1235 container: PrimitiveContainer,
1236 ) -> PrimitiveIndex {
1237 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
1238 let prim_index = self.create_primitive(info, clip_sources, container);
1239
1240 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1241 prim_index
1242 }
1243
push_stacking_context( &mut self, pipeline_id: PipelineId, composite_ops: CompositeOps, transform_style: TransformStyle, is_backface_visible: bool, is_pipeline_root: bool, clip_and_scroll: ScrollNodeAndClipChain, )1244 pub fn push_stacking_context(
1245 &mut self,
1246 pipeline_id: PipelineId,
1247 composite_ops: CompositeOps,
1248 transform_style: TransformStyle,
1249 is_backface_visible: bool,
1250 is_pipeline_root: bool,
1251 clip_and_scroll: ScrollNodeAndClipChain,
1252 ) {
1253 // Construct the necessary set of Picture primitives
1254 // to draw this stacking context.
1255 let current_reference_frame_index = self.current_reference_frame_index();
1256
1257 // An arbitrary large clip rect. For now, we don't
1258 // specify a clip specific to the stacking context.
1259 // However, now that they are represented as Picture
1260 // primitives, we can apply any kind of clip mask
1261 // to them, as for a normal primitive. This is needed
1262 // to correctly handle some CSS cases (see #1957).
1263 let max_clip = LayerRect::max_rect();
1264
1265 // If there is no root picture, create one for the main framebuffer.
1266 if self.sc_stack.is_empty() {
1267 // Should be no pictures at all if the stack is empty...
1268 debug_assert!(self.prim_store.cpu_pictures.is_empty());
1269 debug_assert_eq!(transform_style, TransformStyle::Flat);
1270
1271 // This picture stores primitive runs for items on the
1272 // main framebuffer.
1273 let pic = PicturePrimitive::new_image(
1274 None,
1275 false,
1276 pipeline_id,
1277 current_reference_frame_index,
1278 None,
1279 );
1280
1281 // Add root picture primitive. The provided layer rect
1282 // is zero, because we don't yet know the size of the
1283 // picture. Instead, this is calculated recursively
1284 // when we cull primitives.
1285 let prim_index = self.prim_store.add_primitive(
1286 &LayerRect::zero(),
1287 &max_clip,
1288 true,
1289 None,
1290 None,
1291 PrimitiveContainer::Picture(pic),
1292 );
1293
1294 self.picture_stack.push(prim_index);
1295 } else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 {
1296 // If we have a mix-blend-mode, and we aren't the primary framebuffer,
1297 // the stacking context needs to be isolated to blend correctly as per
1298 // the CSS spec.
1299 // TODO(gw): The way we detect not being the primary framebuffer (len > 2)
1300 // is hacky and depends on how we create a root stacking context
1301 // during flattening.
1302 let current_pic_prim_index = self.picture_stack.last().unwrap();
1303 let pic_cpu_prim_index = self.prim_store.cpu_metadata[current_pic_prim_index.0].cpu_prim_index;
1304 let parent_pic = &mut self.prim_store.cpu_pictures[pic_cpu_prim_index.0];
1305
1306 match parent_pic.kind {
1307 PictureKind::Image { ref mut composite_mode, .. } => {
1308 // If not already isolated for some other reason,
1309 // make this picture as isolated.
1310 if composite_mode.is_none() {
1311 *composite_mode = Some(PictureCompositeMode::Blit);
1312 }
1313 }
1314 PictureKind::TextShadow { .. } => {
1315 panic!("bug: text pictures invalid here");
1316 }
1317 }
1318 }
1319
1320 // Get the transform-style of the parent stacking context,
1321 // which determines if we *might* need to draw this on
1322 // an intermediate surface for plane splitting purposes.
1323 let parent_transform_style = match self.sc_stack.last() {
1324 Some(sc) => sc.transform_style,
1325 None => TransformStyle::Flat,
1326 };
1327
1328 // If this is preserve-3d *or* the parent is, then this stacking
1329 // context is participating in the 3d rendering context. In that
1330 // case, hoist the picture up to the 3d rendering context
1331 // container, so that it's rendered as a sibling with other
1332 // elements in this context.
1333 let participating_in_3d_context =
1334 composite_ops.count() == 0 &&
1335 (parent_transform_style == TransformStyle::Preserve3D ||
1336 transform_style == TransformStyle::Preserve3D);
1337
1338 // If this is participating in a 3d context *and* the
1339 // parent was not a 3d context, then this must be the
1340 // element that establishes a new 3d context.
1341 let establishes_3d_context =
1342 participating_in_3d_context &&
1343 parent_transform_style == TransformStyle::Flat;
1344
1345 let rendering_context_3d_prim_index = if establishes_3d_context {
1346 // If establishing a 3d context, we need to add a picture
1347 // that will be the container for all the planes and any
1348 // un-transformed content.
1349 let container = PicturePrimitive::new_image(
1350 None,
1351 false,
1352 pipeline_id,
1353 current_reference_frame_index,
1354 None,
1355 );
1356
1357 let prim_index = self.prim_store.add_primitive(
1358 &LayerRect::zero(),
1359 &max_clip,
1360 is_backface_visible,
1361 None,
1362 None,
1363 PrimitiveContainer::Picture(container),
1364 );
1365
1366 let parent_pic_prim_index = *self.picture_stack.last().unwrap();
1367 let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1368 let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1369 pic.add_primitive(
1370 prim_index,
1371 clip_and_scroll,
1372 );
1373
1374 self.picture_stack.push(prim_index);
1375
1376 Some(prim_index)
1377 } else {
1378 None
1379 };
1380
1381 let mut parent_pic_prim_index = if !establishes_3d_context && participating_in_3d_context {
1382 // If we're in a 3D context, we will parent the picture
1383 // to the first stacking context we find that is a
1384 // 3D rendering context container. This follows the spec
1385 // by hoisting these items out into the same 3D context
1386 // for plane splitting.
1387 self.sc_stack
1388 .iter()
1389 .rev()
1390 .find(|sc| sc.rendering_context_3d_prim_index.is_some())
1391 .map(|sc| sc.rendering_context_3d_prim_index.unwrap())
1392 .unwrap()
1393 } else {
1394 *self.picture_stack.last().unwrap()
1395 };
1396
1397 // For each filter, create a new image with that composite mode.
1398 for filter in composite_ops.filters.iter().rev() {
1399 let src_prim = PicturePrimitive::new_image(
1400 Some(PictureCompositeMode::Filter(*filter)),
1401 false,
1402 pipeline_id,
1403 current_reference_frame_index,
1404 None,
1405 );
1406
1407 let src_prim_index = self.prim_store.add_primitive(
1408 &LayerRect::zero(),
1409 &max_clip,
1410 is_backface_visible,
1411 None,
1412 None,
1413 PrimitiveContainer::Picture(src_prim),
1414 );
1415
1416 let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1417 parent_pic_prim_index = src_prim_index;
1418 let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1419 pic.add_primitive(
1420 src_prim_index,
1421 clip_and_scroll,
1422 );
1423
1424 self.picture_stack.push(src_prim_index);
1425 }
1426
1427 // Same for mix-blend-mode.
1428 if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
1429 let src_prim = PicturePrimitive::new_image(
1430 Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
1431 false,
1432 pipeline_id,
1433 current_reference_frame_index,
1434 None,
1435 );
1436
1437 let src_prim_index = self.prim_store.add_primitive(
1438 &LayerRect::zero(),
1439 &max_clip,
1440 is_backface_visible,
1441 None,
1442 None,
1443 PrimitiveContainer::Picture(src_prim),
1444 );
1445
1446 let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1447 parent_pic_prim_index = src_prim_index;
1448 let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1449 pic.add_primitive(
1450 src_prim_index,
1451 clip_and_scroll,
1452 );
1453
1454 self.picture_stack.push(src_prim_index);
1455 }
1456
1457 // By default, this picture will be collapsed into
1458 // the owning target.
1459 let mut composite_mode = None;
1460 let mut frame_output_pipeline_id = None;
1461
1462 // If this stacking context if the root of a pipeline, and the caller
1463 // has requested it as an output frame, create a render task to isolate it.
1464 if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) {
1465 composite_mode = Some(PictureCompositeMode::Blit);
1466 frame_output_pipeline_id = Some(pipeline_id);
1467 }
1468
1469 if participating_in_3d_context {
1470 // TODO(gw): For now, as soon as this picture is in
1471 // a 3D context, we draw it to an intermediate
1472 // surface and apply plane splitting. However,
1473 // there is a large optimization opportunity here.
1474 // During culling, we can check if there is actually
1475 // perspective present, and skip the plane splitting
1476 // completely when that is not the case.
1477 composite_mode = Some(PictureCompositeMode::Blit);
1478 }
1479
1480 // Add picture for this actual stacking context contents to render into.
1481 let sc_prim = PicturePrimitive::new_image(
1482 composite_mode,
1483 participating_in_3d_context,
1484 pipeline_id,
1485 current_reference_frame_index,
1486 frame_output_pipeline_id,
1487 );
1488
1489 let sc_prim_index = self.prim_store.add_primitive(
1490 &LayerRect::zero(),
1491 &max_clip,
1492 is_backface_visible,
1493 None,
1494 None,
1495 PrimitiveContainer::Picture(sc_prim),
1496 );
1497
1498 let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1499 let sc_pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1500 sc_pic.add_primitive(
1501 sc_prim_index,
1502 clip_and_scroll,
1503 );
1504
1505 // Add this as the top-most picture for primitives to be added to.
1506 self.picture_stack.push(sc_prim_index);
1507
1508 // TODO(gw): This is super conservative. We can expand on this a lot
1509 // once all the picture code is in place and landed.
1510 let allow_subpixel_aa = composite_ops.count() == 0 &&
1511 transform_style == TransformStyle::Flat;
1512
1513 // Push the SC onto the stack, so we know how to handle things in
1514 // pop_stacking_context.
1515 let sc = FlattenedStackingContext {
1516 composite_ops,
1517 is_backface_visible,
1518 pipeline_id,
1519 allow_subpixel_aa,
1520 transform_style,
1521 rendering_context_3d_prim_index,
1522 };
1523
1524 self.sc_stack.push(sc);
1525 }
1526
pop_stacking_context(&mut self)1527 pub fn pop_stacking_context(&mut self) {
1528 let sc = self.sc_stack.pop().unwrap();
1529
1530 // Always pop at least the main picture for this stacking context.
1531 let mut pop_count = 1;
1532
1533 // Remove the picture for any filter/mix-blend-mode effects.
1534 pop_count += sc.composite_ops.count();
1535
1536 // Remove the 3d context container if created
1537 if sc.rendering_context_3d_prim_index.is_some() {
1538 pop_count += 1;
1539 }
1540
1541 for _ in 0 .. pop_count {
1542 self.picture_stack.pop().expect("bug: mismatched picture stack");
1543 }
1544
1545 // By the time the stacking context stack is empty, we should
1546 // also have cleared the picture stack.
1547 if self.sc_stack.is_empty() {
1548 self.picture_stack.pop().expect("bug: picture stack invalid");
1549 debug_assert!(self.picture_stack.is_empty());
1550 }
1551
1552 assert!(
1553 self.shadow_prim_stack.is_empty(),
1554 "Found unpopped text shadows when popping stacking context!"
1555 );
1556 }
1557
push_reference_frame( &mut self, reference_frame_id: ClipId, parent_id: Option<ClipId>, pipeline_id: PipelineId, rect: &LayerRect, source_transform: Option<PropertyBinding<LayoutTransform>>, source_perspective: Option<LayoutTransform>, origin_in_parent_reference_frame: LayerVector2D, ) -> ClipScrollNodeIndex1558 pub fn push_reference_frame(
1559 &mut self,
1560 reference_frame_id: ClipId,
1561 parent_id: Option<ClipId>,
1562 pipeline_id: PipelineId,
1563 rect: &LayerRect,
1564 source_transform: Option<PropertyBinding<LayoutTransform>>,
1565 source_perspective: Option<LayoutTransform>,
1566 origin_in_parent_reference_frame: LayerVector2D,
1567 ) -> ClipScrollNodeIndex {
1568 let index = self.id_to_index_mapper.get_node_index(reference_frame_id);
1569 let node = ClipScrollNode::new_reference_frame(
1570 parent_id.map(|id| self.id_to_index_mapper.get_node_index(id)),
1571 rect,
1572 source_transform,
1573 source_perspective,
1574 origin_in_parent_reference_frame,
1575 pipeline_id,
1576 );
1577 self.clip_scroll_tree.add_node(node, index);
1578 self.reference_frame_stack.push((reference_frame_id, index));
1579
1580 match parent_id {
1581 Some(ref parent_id) =>
1582 self.id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id),
1583 _ => self.id_to_index_mapper.add_clip_chain(reference_frame_id, ClipChainIndex(0)),
1584 }
1585 index
1586 }
1587
current_reference_frame_index(&self) -> ClipScrollNodeIndex1588 pub fn current_reference_frame_index(&self) -> ClipScrollNodeIndex {
1589 self.reference_frame_stack.last().unwrap().1
1590 }
1591
current_reference_frame_id(&self) -> ClipId1592 pub fn current_reference_frame_id(&self) -> ClipId{
1593 self.reference_frame_stack.last().unwrap().0
1594 }
1595
setup_viewport_offset( &mut self, inner_rect: DeviceUintRect, device_pixel_scale: DevicePixelScale, )1596 pub fn setup_viewport_offset(
1597 &mut self,
1598 inner_rect: DeviceUintRect,
1599 device_pixel_scale: DevicePixelScale,
1600 ) {
1601 let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round();
1602 let root_id = self.clip_scroll_tree.root_reference_frame_index();
1603 let root_node = &mut self.clip_scroll_tree.nodes[root_id.0];
1604 if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type {
1605 info.resolved_transform =
1606 LayerVector2D::new(viewport_offset.x, viewport_offset.y).into();
1607 }
1608 }
1609
push_root( &mut self, pipeline_id: PipelineId, viewport_size: &LayerSize, content_size: &LayerSize, )1610 pub fn push_root(
1611 &mut self,
1612 pipeline_id: PipelineId,
1613 viewport_size: &LayerSize,
1614 content_size: &LayerSize,
1615 ) {
1616 let viewport_rect = LayerRect::new(LayerPoint::zero(), *viewport_size);
1617
1618 self.push_reference_frame(
1619 ClipId::root_reference_frame(pipeline_id),
1620 None,
1621 pipeline_id,
1622 &viewport_rect,
1623 None,
1624 None,
1625 LayerVector2D::zero(),
1626 );
1627
1628 self.add_scroll_frame(
1629 ClipId::root_scroll_node(pipeline_id),
1630 ClipId::root_reference_frame(pipeline_id),
1631 Some(ExternalScrollId(0, pipeline_id)),
1632 pipeline_id,
1633 &viewport_rect,
1634 content_size,
1635 ScrollSensitivity::ScriptAndInputEvents,
1636 );
1637 }
1638
add_clip_node( &mut self, new_node_id: ClipId, parent_id: ClipId, clip_region: ClipRegion, ) -> ClipScrollNodeIndex1639 pub fn add_clip_node(
1640 &mut self,
1641 new_node_id: ClipId,
1642 parent_id: ClipId,
1643 clip_region: ClipRegion,
1644 ) -> ClipScrollNodeIndex {
1645 let clip_rect = clip_region.main;
1646 let clip_sources = ClipSources::from(clip_region);
1647 let handle = self.clip_store.insert(clip_sources);
1648
1649 let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
1650 let clip_chain_index = self.clip_scroll_tree.add_clip_node(
1651 node_index,
1652 self.id_to_index_mapper.get_node_index(parent_id),
1653 handle,
1654 clip_rect,
1655 new_node_id.pipeline_id(),
1656 );
1657 self.id_to_index_mapper.add_clip_chain(new_node_id, clip_chain_index);
1658 node_index
1659 }
1660
add_scroll_frame( &mut self, new_node_id: ClipId, parent_id: ClipId, external_id: Option<ExternalScrollId>, pipeline_id: PipelineId, frame_rect: &LayerRect, content_size: &LayerSize, scroll_sensitivity: ScrollSensitivity, ) -> ClipScrollNodeIndex1661 pub fn add_scroll_frame(
1662 &mut self,
1663 new_node_id: ClipId,
1664 parent_id: ClipId,
1665 external_id: Option<ExternalScrollId>,
1666 pipeline_id: PipelineId,
1667 frame_rect: &LayerRect,
1668 content_size: &LayerSize,
1669 scroll_sensitivity: ScrollSensitivity,
1670 ) -> ClipScrollNodeIndex {
1671 let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
1672 let node = ClipScrollNode::new_scroll_frame(
1673 pipeline_id,
1674 self.id_to_index_mapper.get_node_index(parent_id),
1675 external_id,
1676 frame_rect,
1677 content_size,
1678 scroll_sensitivity,
1679 );
1680
1681 self.clip_scroll_tree.add_node(node, node_index);
1682 self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
1683 node_index
1684 }
1685
pop_reference_frame(&mut self)1686 pub fn pop_reference_frame(&mut self) {
1687 self.reference_frame_stack.pop();
1688 }
1689
push_shadow( &mut self, shadow: Shadow, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, )1690 pub fn push_shadow(
1691 &mut self,
1692 shadow: Shadow,
1693 clip_and_scroll: ScrollNodeAndClipChain,
1694 info: &LayerPrimitiveInfo,
1695 ) {
1696 let pipeline_id = self.sc_stack.last().unwrap().pipeline_id;
1697 let prim = PicturePrimitive::new_text_shadow(shadow, pipeline_id);
1698
1699 // Create an empty shadow primitive. Insert it into
1700 // the draw lists immediately so that it will be drawn
1701 // before any visual text elements that are added as
1702 // part of this shadow context.
1703 let prim_index = self.create_primitive(
1704 info,
1705 Vec::new(),
1706 PrimitiveContainer::Picture(prim),
1707 );
1708
1709 let pending = vec![(prim_index, clip_and_scroll)];
1710 self.shadow_prim_stack.push((prim_index, pending));
1711 }
1712
pop_all_shadows(&mut self)1713 pub fn pop_all_shadows(&mut self) {
1714 assert!(self.shadow_prim_stack.len() > 0, "popped shadows, but none were present");
1715
1716 // Borrowcheck dance
1717 let mut shadows = mem::replace(&mut self.shadow_prim_stack, Vec::new());
1718 for (_, pending_primitives) in shadows.drain(..) {
1719 // Push any fast-path shadows now
1720 for (prim_index, clip_and_scroll) in pending_primitives {
1721 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1722 }
1723 }
1724
1725 let mut pending_primitives = mem::replace(&mut self.pending_shadow_contents, Vec::new());
1726 for (prim_index, clip_and_scroll, info) in pending_primitives.drain(..) {
1727 self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
1728 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1729 }
1730
1731 mem::replace(&mut self.pending_shadow_contents, pending_primitives);
1732 mem::replace(&mut self.shadow_prim_stack, shadows);
1733 }
1734
add_solid_rectangle( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, color: ColorF, segments: Option<BrushSegmentDescriptor>, )1735 pub fn add_solid_rectangle(
1736 &mut self,
1737 clip_and_scroll: ScrollNodeAndClipChain,
1738 info: &LayerPrimitiveInfo,
1739 color: ColorF,
1740 segments: Option<BrushSegmentDescriptor>,
1741 ) {
1742 if color.a == 0.0 {
1743 // Don't add transparent rectangles to the draw list, but do consider them for hit
1744 // testing. This allows specifying invisible hit testing areas.
1745 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
1746 return;
1747 }
1748
1749 let prim = BrushPrimitive::new(
1750 BrushKind::Solid {
1751 color,
1752 },
1753 segments,
1754 );
1755
1756 self.add_primitive(
1757 clip_and_scroll,
1758 info,
1759 Vec::new(),
1760 PrimitiveContainer::Brush(prim),
1761 );
1762 }
1763
add_clear_rectangle( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, )1764 pub fn add_clear_rectangle(
1765 &mut self,
1766 clip_and_scroll: ScrollNodeAndClipChain,
1767 info: &LayerPrimitiveInfo,
1768 ) {
1769 let prim = BrushPrimitive::new(
1770 BrushKind::Clear,
1771 None,
1772 );
1773
1774 self.add_primitive(
1775 clip_and_scroll,
1776 info,
1777 Vec::new(),
1778 PrimitiveContainer::Brush(prim),
1779 );
1780 }
1781
add_scroll_bar( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, color: ColorF, scrollbar_info: ScrollbarInfo, )1782 pub fn add_scroll_bar(
1783 &mut self,
1784 clip_and_scroll: ScrollNodeAndClipChain,
1785 info: &LayerPrimitiveInfo,
1786 color: ColorF,
1787 scrollbar_info: ScrollbarInfo,
1788 ) {
1789 if color.a == 0.0 {
1790 return;
1791 }
1792
1793 let prim = BrushPrimitive::new(
1794 BrushKind::Solid {
1795 color,
1796 },
1797 None,
1798 );
1799
1800 let prim_index = self.add_primitive(
1801 clip_and_scroll,
1802 info,
1803 Vec::new(),
1804 PrimitiveContainer::Brush(prim),
1805 );
1806
1807 self.scrollbar_prims.push(ScrollbarPrimitive {
1808 prim_index,
1809 scroll_frame_index: scrollbar_info.0,
1810 frame_rect: scrollbar_info.1,
1811 });
1812 }
1813
add_line( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, wavy_line_thickness: f32, orientation: LineOrientation, line_color: &ColorF, style: LineStyle, )1814 pub fn add_line(
1815 &mut self,
1816 clip_and_scroll: ScrollNodeAndClipChain,
1817 info: &LayerPrimitiveInfo,
1818 wavy_line_thickness: f32,
1819 orientation: LineOrientation,
1820 line_color: &ColorF,
1821 style: LineStyle,
1822 ) {
1823 let line = BrushPrimitive::new(
1824 BrushKind::Line {
1825 wavy_line_thickness,
1826 color: line_color.premultiplied(),
1827 style,
1828 orientation,
1829 },
1830 None,
1831 );
1832
1833 let mut fast_shadow_prims = Vec::new();
1834 for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
1835 let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
1836 let picture = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
1837 match picture.kind {
1838 PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
1839 fast_shadow_prims.push((idx, offset, color));
1840 }
1841 _ => {}
1842 }
1843 }
1844
1845 for (idx, shadow_offset, shadow_color) in fast_shadow_prims {
1846 let line = BrushPrimitive::new(
1847 BrushKind::Line {
1848 wavy_line_thickness,
1849 color: shadow_color.premultiplied(),
1850 style,
1851 orientation,
1852 },
1853 None,
1854 );
1855 let mut info = info.clone();
1856 info.rect = info.rect.translate(&shadow_offset);
1857 info.local_clip =
1858 LocalClip::from(info.local_clip.clip_rect().translate(&shadow_offset));
1859 let prim_index = self.create_primitive(
1860 &info,
1861 Vec::new(),
1862 PrimitiveContainer::Brush(line),
1863 );
1864 self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
1865 }
1866
1867 let prim_index = self.create_primitive(
1868 &info,
1869 Vec::new(),
1870 PrimitiveContainer::Brush(line),
1871 );
1872
1873 if line_color.a > 0.0 {
1874 if self.shadow_prim_stack.is_empty() {
1875 self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
1876 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1877 } else {
1878 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
1879 }
1880 }
1881
1882 for &(shadow_prim_index, _) in &self.shadow_prim_stack {
1883 let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
1884 debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
1885 let picture =
1886 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
1887
1888 match picture.kind {
1889 // Only run real blurs here (fast path zero blurs are handled above).
1890 PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
1891 picture.add_primitive(
1892 prim_index,
1893 clip_and_scroll,
1894 );
1895 }
1896 _ => {}
1897 }
1898 }
1899 }
1900
add_border( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, border_item: &BorderDisplayItem, gradient_stops: ItemRange<GradientStop>, gradient_stops_count: usize, )1901 pub fn add_border(
1902 &mut self,
1903 clip_and_scroll: ScrollNodeAndClipChain,
1904 info: &LayerPrimitiveInfo,
1905 border_item: &BorderDisplayItem,
1906 gradient_stops: ItemRange<GradientStop>,
1907 gradient_stops_count: usize,
1908 ) {
1909 let rect = info.rect;
1910 let create_segments = |outset: SideOffsets2D<f32>| {
1911 // Calculate the modified rect as specific by border-image-outset
1912 let origin = LayerPoint::new(rect.origin.x - outset.left, rect.origin.y - outset.top);
1913 let size = LayerSize::new(
1914 rect.size.width + outset.left + outset.right,
1915 rect.size.height + outset.top + outset.bottom,
1916 );
1917 let rect = LayerRect::new(origin, size);
1918
1919 let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
1920 let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
1921
1922 let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
1923 let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
1924
1925 let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
1926 let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
1927
1928 let br_outer = LayerPoint::new(
1929 rect.origin.x + rect.size.width,
1930 rect.origin.y + rect.size.height,
1931 );
1932 let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
1933
1934 // Build the list of gradient segments
1935 vec![
1936 // Top left
1937 LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
1938 // Top right
1939 LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
1940 // Bottom right
1941 LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
1942 // Bottom left
1943 LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
1944 // Top
1945 LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
1946 // Bottom
1947 LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
1948 // Left
1949 LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
1950 // Right
1951 LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
1952 ]
1953 };
1954
1955 match border_item.details {
1956 BorderDetails::Image(ref border) => {
1957 // Calculate the modified rect as specific by border-image-outset
1958 let origin = LayerPoint::new(
1959 rect.origin.x - border.outset.left,
1960 rect.origin.y - border.outset.top,
1961 );
1962 let size = LayerSize::new(
1963 rect.size.width + border.outset.left + border.outset.right,
1964 rect.size.height + border.outset.top + border.outset.bottom,
1965 );
1966 let rect = LayerRect::new(origin, size);
1967
1968 // Calculate the local texel coords of the slices.
1969 let px0 = 0.0;
1970 let px1 = border.patch.slice.left as f32;
1971 let px2 = border.patch.width as f32 - border.patch.slice.right as f32;
1972 let px3 = border.patch.width as f32;
1973
1974 let py0 = 0.0;
1975 let py1 = border.patch.slice.top as f32;
1976 let py2 = border.patch.height as f32 - border.patch.slice.bottom as f32;
1977 let py3 = border.patch.height as f32;
1978
1979 let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
1980 let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
1981
1982 let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
1983 let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
1984
1985 let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
1986 let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
1987
1988 let br_outer = LayerPoint::new(
1989 rect.origin.x + rect.size.width,
1990 rect.origin.y + rect.size.height,
1991 );
1992 let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
1993
1994 fn add_segment(
1995 segments: &mut Vec<ImageBorderSegment>,
1996 rect: LayerRect,
1997 uv_rect: TexelRect,
1998 repeat_horizontal: RepeatMode,
1999 repeat_vertical: RepeatMode) {
2000 if uv_rect.uv1.x > uv_rect.uv0.x &&
2001 uv_rect.uv1.y > uv_rect.uv0.y {
2002 segments.push(ImageBorderSegment::new(
2003 rect,
2004 uv_rect,
2005 repeat_horizontal,
2006 repeat_vertical,
2007 ));
2008 }
2009 }
2010
2011 // Build the list of image segments
2012 let mut segments = vec![];
2013
2014 // Top left
2015 add_segment(
2016 &mut segments,
2017 LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
2018 TexelRect::new(px0, py0, px1, py1),
2019 RepeatMode::Stretch,
2020 RepeatMode::Stretch
2021 );
2022 // Top right
2023 add_segment(
2024 &mut segments,
2025 LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
2026 TexelRect::new(px2, py0, px3, py1),
2027 RepeatMode::Stretch,
2028 RepeatMode::Stretch
2029 );
2030 // Bottom right
2031 add_segment(
2032 &mut segments,
2033 LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
2034 TexelRect::new(px2, py2, px3, py3),
2035 RepeatMode::Stretch,
2036 RepeatMode::Stretch
2037 );
2038 // Bottom left
2039 add_segment(
2040 &mut segments,
2041 LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
2042 TexelRect::new(px0, py2, px1, py3),
2043 RepeatMode::Stretch,
2044 RepeatMode::Stretch
2045 );
2046
2047 // Center
2048 if border.fill {
2049 add_segment(
2050 &mut segments,
2051 LayerRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
2052 TexelRect::new(px1, py1, px2, py2),
2053 border.repeat_horizontal,
2054 border.repeat_vertical
2055 );
2056 }
2057
2058 // Add edge segments.
2059
2060 // Top
2061 add_segment(
2062 &mut segments,
2063 LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
2064 TexelRect::new(px1, py0, px2, py1),
2065 border.repeat_horizontal,
2066 RepeatMode::Stretch,
2067 );
2068 // Bottom
2069 add_segment(
2070 &mut segments,
2071 LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
2072 TexelRect::new(px1, py2, px2, py3),
2073 border.repeat_horizontal,
2074 RepeatMode::Stretch,
2075 );
2076 // Left
2077 add_segment(
2078 &mut segments,
2079 LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
2080 TexelRect::new(px0, py1, px1, py2),
2081 RepeatMode::Stretch,
2082 border.repeat_vertical,
2083 );
2084 // Right
2085 add_segment(
2086 &mut segments,
2087 LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
2088 TexelRect::new(px2, py1, px3, py2),
2089 RepeatMode::Stretch,
2090 border.repeat_vertical,
2091 );
2092
2093 for segment in segments {
2094 let mut info = info.clone();
2095 info.rect = segment.geom_rect;
2096 self.add_image(
2097 clip_and_scroll,
2098 &info,
2099 segment.stretch_size,
2100 segment.tile_spacing,
2101 Some(segment.sub_rect),
2102 border.image_key,
2103 ImageRendering::Auto,
2104 AlphaType::PremultipliedAlpha,
2105 None,
2106 );
2107 }
2108 }
2109 BorderDetails::Normal(ref border) => {
2110 self.add_normal_border(info, border, &border_item.widths, clip_and_scroll);
2111 }
2112 BorderDetails::Gradient(ref border) => for segment in create_segments(border.outset) {
2113 let segment_rel = segment.origin - rect.origin;
2114 let mut info = info.clone();
2115 info.rect = segment;
2116
2117 self.add_gradient(
2118 clip_and_scroll,
2119 &info,
2120 border.gradient.start_point - segment_rel,
2121 border.gradient.end_point - segment_rel,
2122 gradient_stops,
2123 gradient_stops_count,
2124 border.gradient.extend_mode,
2125 segment.size,
2126 LayerSize::zero(),
2127 );
2128 },
2129 BorderDetails::RadialGradient(ref border) => {
2130 for segment in create_segments(border.outset) {
2131 let segment_rel = segment.origin - rect.origin;
2132 let mut info = info.clone();
2133 info.rect = segment;
2134
2135 self.add_radial_gradient(
2136 clip_and_scroll,
2137 &info,
2138 border.gradient.start_center - segment_rel,
2139 border.gradient.start_radius,
2140 border.gradient.end_center - segment_rel,
2141 border.gradient.end_radius,
2142 border.gradient.ratio_xy,
2143 gradient_stops,
2144 border.gradient.extend_mode,
2145 segment.size,
2146 LayerSize::zero(),
2147 );
2148 }
2149 }
2150 }
2151 }
2152
add_gradient_impl( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_point: LayerPoint, end_point: LayerPoint, stops: ItemRange<GradientStop>, stops_count: usize, extend_mode: ExtendMode, gradient_index: CachedGradientIndex, )2153 fn add_gradient_impl(
2154 &mut self,
2155 clip_and_scroll: ScrollNodeAndClipChain,
2156 info: &LayerPrimitiveInfo,
2157 start_point: LayerPoint,
2158 end_point: LayerPoint,
2159 stops: ItemRange<GradientStop>,
2160 stops_count: usize,
2161 extend_mode: ExtendMode,
2162 gradient_index: CachedGradientIndex,
2163 ) {
2164 // Try to ensure that if the gradient is specified in reverse, then so long as the stops
2165 // are also supplied in reverse that the rendered result will be equivalent. To do this,
2166 // a reference orientation for the gradient line must be chosen, somewhat arbitrarily, so
2167 // just designate the reference orientation as start < end. Aligned gradient rendering
2168 // manages to produce the same result regardless of orientation, so don't worry about
2169 // reversing in that case.
2170 let reverse_stops = start_point.x > end_point.x ||
2171 (start_point.x == end_point.x && start_point.y > end_point.y);
2172
2173 // To get reftests exactly matching with reverse start/end
2174 // points, it's necessary to reverse the gradient
2175 // line in some cases.
2176 let (sp, ep) = if reverse_stops {
2177 (end_point, start_point)
2178 } else {
2179 (start_point, end_point)
2180 };
2181
2182 let prim = BrushPrimitive::new(
2183 BrushKind::LinearGradient {
2184 stops_range: stops,
2185 stops_count,
2186 extend_mode,
2187 reverse_stops,
2188 start_point: sp,
2189 end_point: ep,
2190 gradient_index,
2191 },
2192 None,
2193 );
2194
2195 let prim = PrimitiveContainer::Brush(prim);
2196
2197 self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
2198 }
2199
add_gradient( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_point: LayerPoint, end_point: LayerPoint, stops: ItemRange<GradientStop>, stops_count: usize, extend_mode: ExtendMode, tile_size: LayerSize, tile_spacing: LayerSize, )2200 pub fn add_gradient(
2201 &mut self,
2202 clip_and_scroll: ScrollNodeAndClipChain,
2203 info: &LayerPrimitiveInfo,
2204 start_point: LayerPoint,
2205 end_point: LayerPoint,
2206 stops: ItemRange<GradientStop>,
2207 stops_count: usize,
2208 extend_mode: ExtendMode,
2209 tile_size: LayerSize,
2210 tile_spacing: LayerSize,
2211 ) {
2212 let gradient_index = CachedGradientIndex(self.cached_gradients.len());
2213 self.cached_gradients.push(CachedGradient::new());
2214
2215 let prim_infos = info.decompose(
2216 tile_size,
2217 tile_spacing,
2218 64 * 64,
2219 );
2220
2221 if prim_infos.is_empty() {
2222 self.add_gradient_impl(
2223 clip_and_scroll,
2224 info,
2225 start_point,
2226 end_point,
2227 stops,
2228 stops_count,
2229 extend_mode,
2230 gradient_index,
2231 );
2232 } else {
2233 for prim_info in prim_infos {
2234 self.add_gradient_impl(
2235 clip_and_scroll,
2236 &prim_info,
2237 start_point,
2238 end_point,
2239 stops,
2240 stops_count,
2241 extend_mode,
2242 gradient_index,
2243 );
2244 }
2245 }
2246 }
2247
add_radial_gradient_impl( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_center: LayerPoint, start_radius: f32, end_center: LayerPoint, end_radius: f32, ratio_xy: f32, stops: ItemRange<GradientStop>, extend_mode: ExtendMode, gradient_index: CachedGradientIndex, )2248 fn add_radial_gradient_impl(
2249 &mut self,
2250 clip_and_scroll: ScrollNodeAndClipChain,
2251 info: &LayerPrimitiveInfo,
2252 start_center: LayerPoint,
2253 start_radius: f32,
2254 end_center: LayerPoint,
2255 end_radius: f32,
2256 ratio_xy: f32,
2257 stops: ItemRange<GradientStop>,
2258 extend_mode: ExtendMode,
2259 gradient_index: CachedGradientIndex,
2260 ) {
2261 let prim = BrushPrimitive::new(
2262 BrushKind::RadialGradient {
2263 stops_range: stops,
2264 extend_mode,
2265 start_center,
2266 end_center,
2267 start_radius,
2268 end_radius,
2269 ratio_xy,
2270 gradient_index,
2271 },
2272 None,
2273 );
2274
2275 self.add_primitive(
2276 clip_and_scroll,
2277 info,
2278 Vec::new(),
2279 PrimitiveContainer::Brush(prim),
2280 );
2281 }
2282
add_radial_gradient( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_center: LayerPoint, start_radius: f32, end_center: LayerPoint, end_radius: f32, ratio_xy: f32, stops: ItemRange<GradientStop>, extend_mode: ExtendMode, tile_size: LayerSize, tile_spacing: LayerSize, )2283 pub fn add_radial_gradient(
2284 &mut self,
2285 clip_and_scroll: ScrollNodeAndClipChain,
2286 info: &LayerPrimitiveInfo,
2287 start_center: LayerPoint,
2288 start_radius: f32,
2289 end_center: LayerPoint,
2290 end_radius: f32,
2291 ratio_xy: f32,
2292 stops: ItemRange<GradientStop>,
2293 extend_mode: ExtendMode,
2294 tile_size: LayerSize,
2295 tile_spacing: LayerSize,
2296 ) {
2297 let gradient_index = CachedGradientIndex(self.cached_gradients.len());
2298 self.cached_gradients.push(CachedGradient::new());
2299
2300 let prim_infos = info.decompose(
2301 tile_size,
2302 tile_spacing,
2303 64 * 64,
2304 );
2305
2306 if prim_infos.is_empty() {
2307 self.add_radial_gradient_impl(
2308 clip_and_scroll,
2309 info,
2310 start_center,
2311 start_radius,
2312 end_center,
2313 end_radius,
2314 ratio_xy,
2315 stops,
2316 extend_mode,
2317 gradient_index,
2318 );
2319 } else {
2320 for prim_info in prim_infos {
2321 self.add_radial_gradient_impl(
2322 clip_and_scroll,
2323 &prim_info,
2324 start_center,
2325 start_radius,
2326 end_center,
2327 end_radius,
2328 ratio_xy,
2329 stops,
2330 extend_mode,
2331 gradient_index,
2332 );
2333 }
2334 }
2335 }
2336
add_text( &mut self, clip_and_scroll: ScrollNodeAndClipChain, run_offset: LayoutVector2D, info: &LayerPrimitiveInfo, font_instance_key: &FontInstanceKey, text_color: &ColorF, glyph_range: ItemRange<GlyphInstance>, glyph_count: usize, glyph_options: Option<GlyphOptions>, )2337 pub fn add_text(
2338 &mut self,
2339 clip_and_scroll: ScrollNodeAndClipChain,
2340 run_offset: LayoutVector2D,
2341 info: &LayerPrimitiveInfo,
2342 font_instance_key: &FontInstanceKey,
2343 text_color: &ColorF,
2344 glyph_range: ItemRange<GlyphInstance>,
2345 glyph_count: usize,
2346 glyph_options: Option<GlyphOptions>,
2347 ) {
2348 let prim = {
2349 let instance_map = self.font_instances.read().unwrap();
2350 let font_instance = match instance_map.get(&font_instance_key) {
2351 Some(instance) => instance,
2352 None => {
2353 warn!("Unknown font instance key");
2354 debug!("key={:?}", font_instance_key);
2355 return;
2356 }
2357 };
2358
2359 // Trivial early out checks
2360 if font_instance.size.0 <= 0 {
2361 return;
2362 }
2363
2364 // Sanity check - anything with glyphs bigger than this
2365 // is probably going to consume too much memory to render
2366 // efficiently anyway. This is specifically to work around
2367 // the font_advance.html reftest, which creates a very large
2368 // font as a crash test - the rendering is also ignored
2369 // by the azure renderer.
2370 if font_instance.size >= Au::from_px(4096) {
2371 return;
2372 }
2373
2374 // TODO(gw): Use a proper algorithm to select
2375 // whether this item should be rendered with
2376 // subpixel AA!
2377 let mut render_mode = self.config
2378 .default_font_render_mode
2379 .limit_by(font_instance.render_mode);
2380 let mut flags = font_instance.flags;
2381 if let Some(options) = glyph_options {
2382 render_mode = render_mode.limit_by(options.render_mode);
2383 flags |= options.flags;
2384 }
2385
2386 // There are some conditions under which we can't use
2387 // subpixel text rendering, even if enabled.
2388 if render_mode == FontRenderMode::Subpixel {
2389 // text on a picture that has filters
2390 // (e.g. opacity) can't use sub-pixel.
2391 // TODO(gw): It's possible we can relax this in
2392 // the future, if we modify the way
2393 // we handle subpixel blending.
2394 if let Some(ref stacking_context) = self.sc_stack.last() {
2395 if !stacking_context.allow_subpixel_aa {
2396 render_mode = FontRenderMode::Alpha;
2397 }
2398 }
2399 }
2400
2401 let prim_font = FontInstance::new(
2402 font_instance.font_key,
2403 font_instance.size,
2404 *text_color,
2405 font_instance.bg_color,
2406 render_mode,
2407 font_instance.subpx_dir,
2408 flags,
2409 font_instance.platform_options,
2410 font_instance.variations.clone(),
2411 );
2412 TextRunPrimitiveCpu {
2413 font: prim_font,
2414 glyph_range,
2415 glyph_count,
2416 glyph_gpu_blocks: Vec::new(),
2417 glyph_keys: Vec::new(),
2418 offset: run_offset,
2419 shadow: false,
2420 }
2421 };
2422
2423 // Text shadows that have a blur radius of 0 need to be rendered as normal
2424 // text elements to get pixel perfect results for reftests. It's also a big
2425 // performance win to avoid blurs and render target allocations where
2426 // possible. For any text shadows that have zero blur, create a normal text
2427 // primitive with the shadow's color and offset. These need to be added
2428 // *before* the visual text primitive in order to get the correct paint
2429 // order. Store them in a Vec first to work around borrowck issues.
2430 // TODO(gw): Refactor to avoid having to store them in a Vec first.
2431 let mut fast_shadow_prims = Vec::new();
2432 for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
2433 let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
2434 let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
2435 match picture_prim.kind {
2436 PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
2437 let mut text_prim = prim.clone();
2438 text_prim.font.color = color.into();
2439 text_prim.shadow = true;
2440 text_prim.offset += offset;
2441 fast_shadow_prims.push((idx, text_prim));
2442 }
2443 _ => {}
2444 }
2445 }
2446
2447 for (idx, text_prim) in fast_shadow_prims {
2448 let rect = info.rect;
2449 let mut info = info.clone();
2450 info.rect = rect.translate(&text_prim.offset);
2451 info.local_clip =
2452 LocalClip::from(info.local_clip.clip_rect().translate(&text_prim.offset));
2453 let prim_index = self.create_primitive(
2454 &info,
2455 Vec::new(),
2456 PrimitiveContainer::TextRun(text_prim),
2457 );
2458 self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
2459 }
2460
2461 // Create (and add to primitive store) the primitive that will be
2462 // used for both the visual element and also the shadow(s).
2463 let prim_index = self.create_primitive(
2464 info,
2465 Vec::new(),
2466 PrimitiveContainer::TextRun(prim),
2467 );
2468
2469 // Only add a visual element if it can contribute to the scene.
2470 if text_color.a > 0.0 {
2471 if self.shadow_prim_stack.is_empty() {
2472 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
2473 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
2474 } else {
2475 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
2476 }
2477 }
2478
2479 // Now add this primitive index to all the currently active text shadow
2480 // primitives. Although we're adding the indices *after* the visual
2481 // primitive here, they will still draw before the visual text, since
2482 // the shadow primitive itself has been added to the draw cmd
2483 // list *before* the visual element, during push_shadow. We need
2484 // the primitive index of the visual element here before we can add
2485 // the indices as sub-primitives to the shadow primitives.
2486 for &(shadow_prim_index, _) in &self.shadow_prim_stack {
2487 let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
2488 debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
2489 let picture =
2490 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
2491
2492 match picture.kind {
2493 // Only run real blurs here (fast path zero blurs are handled above).
2494 PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
2495 picture.add_primitive(
2496 prim_index,
2497 clip_and_scroll,
2498 );
2499 }
2500 _ => {}
2501 }
2502 }
2503 }
2504
add_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, stretch_size: LayerSize, mut tile_spacing: LayerSize, sub_rect: Option<TexelRect>, image_key: ImageKey, image_rendering: ImageRendering, alpha_type: AlphaType, tile_offset: Option<TileOffset>, )2505 pub fn add_image(
2506 &mut self,
2507 clip_and_scroll: ScrollNodeAndClipChain,
2508 info: &LayerPrimitiveInfo,
2509 stretch_size: LayerSize,
2510 mut tile_spacing: LayerSize,
2511 sub_rect: Option<TexelRect>,
2512 image_key: ImageKey,
2513 image_rendering: ImageRendering,
2514 alpha_type: AlphaType,
2515 tile_offset: Option<TileOffset>,
2516 ) {
2517 // If the tile spacing is the same as the rect size,
2518 // then it is effectively zero. We use this later on
2519 // in prim_store to detect if an image can be considered
2520 // opaque.
2521 if tile_spacing == info.rect.size {
2522 tile_spacing = LayerSize::zero();
2523 }
2524
2525 let request = ImageRequest {
2526 key: image_key,
2527 rendering: image_rendering,
2528 tile: tile_offset,
2529 };
2530
2531 // See if conditions are met to run through the new
2532 // image brush shader, which supports segments.
2533 if tile_spacing == LayerSize::zero() &&
2534 stretch_size == info.rect.size &&
2535 sub_rect.is_none() &&
2536 tile_offset.is_none() {
2537 let prim = BrushPrimitive::new(
2538 BrushKind::Image {
2539 request,
2540 current_epoch: Epoch::invalid(),
2541 alpha_type,
2542 },
2543 None,
2544 );
2545
2546 self.add_primitive(
2547 clip_and_scroll,
2548 info,
2549 Vec::new(),
2550 PrimitiveContainer::Brush(prim),
2551 );
2552 } else {
2553 let prim_cpu = ImagePrimitiveCpu {
2554 tile_spacing,
2555 alpha_type,
2556 stretch_size,
2557 current_epoch: Epoch::invalid(),
2558 source: ImageSource::Default,
2559 key: ImageCacheKey {
2560 request,
2561 texel_rect: sub_rect.map(|texel_rect| {
2562 DeviceIntRect::new(
2563 DeviceIntPoint::new(
2564 texel_rect.uv0.x as i32,
2565 texel_rect.uv0.y as i32,
2566 ),
2567 DeviceIntSize::new(
2568 (texel_rect.uv1.x - texel_rect.uv0.x) as i32,
2569 (texel_rect.uv1.y - texel_rect.uv0.y) as i32,
2570 ),
2571 )
2572 }),
2573 },
2574 };
2575
2576 self.add_primitive(
2577 clip_and_scroll,
2578 info,
2579 Vec::new(),
2580 PrimitiveContainer::Image(prim_cpu),
2581 );
2582 }
2583 }
2584
add_yuv_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, yuv_data: YuvData, color_space: YuvColorSpace, image_rendering: ImageRendering, )2585 pub fn add_yuv_image(
2586 &mut self,
2587 clip_and_scroll: ScrollNodeAndClipChain,
2588 info: &LayerPrimitiveInfo,
2589 yuv_data: YuvData,
2590 color_space: YuvColorSpace,
2591 image_rendering: ImageRendering,
2592 ) {
2593 let format = yuv_data.get_format();
2594 let yuv_key = match yuv_data {
2595 YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
2596 YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
2597 YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
2598 };
2599
2600 let prim = BrushPrimitive::new(
2601 BrushKind::YuvImage {
2602 yuv_key,
2603 format,
2604 color_space,
2605 image_rendering,
2606 },
2607 None,
2608 );
2609
2610 self.add_primitive(
2611 clip_and_scroll,
2612 info,
2613 Vec::new(),
2614 PrimitiveContainer::Brush(prim),
2615 );
2616 }
2617 }
2618
build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene2619 pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene {
2620
2621 let mut clip_scroll_tree = ClipScrollTree::new();
2622 let mut new_scene = Scene::new();
2623
2624 let frame_builder = DisplayListFlattener::create_frame_builder(
2625 FrameBuilder::empty(), // WIP, we're not really recycling anything here, clean this up.
2626 &request.scene,
2627 &mut clip_scroll_tree,
2628 request.font_instances,
2629 request.tiled_image_map,
2630 &request.view,
2631 &request.output_pipelines,
2632 config,
2633 &mut new_scene
2634 );
2635
2636 BuiltScene {
2637 scene: new_scene,
2638 frame_builder,
2639 clip_scroll_tree,
2640 removed_pipelines: request.removed_pipelines,
2641 }
2642 }
2643
2644 trait PrimitiveInfoTiler {
decompose( &self, tile_size: LayerSize, tile_spacing: LayerSize, max_prims: usize, ) -> Vec<LayerPrimitiveInfo>2645 fn decompose(
2646 &self,
2647 tile_size: LayerSize,
2648 tile_spacing: LayerSize,
2649 max_prims: usize,
2650 ) -> Vec<LayerPrimitiveInfo>;
2651 }
2652
2653 impl PrimitiveInfoTiler for LayerPrimitiveInfo {
decompose( &self, tile_size: LayerSize, tile_spacing: LayerSize, max_prims: usize, ) -> Vec<LayerPrimitiveInfo>2654 fn decompose(
2655 &self,
2656 tile_size: LayerSize,
2657 tile_spacing: LayerSize,
2658 max_prims: usize,
2659 ) -> Vec<LayerPrimitiveInfo> {
2660 let mut prims = Vec::new();
2661 let tile_repeat = tile_size + tile_spacing;
2662
2663 if tile_repeat.width <= 0.0 ||
2664 tile_repeat.height <= 0.0 {
2665 return prims;
2666 }
2667
2668 if tile_repeat.width < self.rect.size.width ||
2669 tile_repeat.height < self.rect.size.height {
2670 let local_clip = self.local_clip.clip_by(&self.rect);
2671 let rect_p0 = self.rect.origin;
2672 let rect_p1 = self.rect.bottom_right();
2673
2674 let mut y0 = rect_p0.y;
2675 while y0 < rect_p1.y {
2676 let mut x0 = rect_p0.x;
2677
2678 while x0 < rect_p1.x {
2679 prims.push(LayerPrimitiveInfo {
2680 rect: LayerRect::new(
2681 LayerPoint::new(x0, y0),
2682 tile_size,
2683 ),
2684 local_clip,
2685 is_backface_visible: self.is_backface_visible,
2686 tag: self.tag,
2687 });
2688
2689 // Mostly a safety against a crazy number of primitives
2690 // being generated. If we exceed that amount, just bail
2691 // out and only draw the maximum amount.
2692 if prims.len() > max_prims {
2693 warn!("too many prims found due to repeat/tile. dropping extra prims!");
2694 return prims;
2695 }
2696
2697 x0 += tile_repeat.width;
2698 }
2699
2700 y0 += tile_repeat.height;
2701 }
2702 }
2703
2704 prims
2705 }
2706 }
2707
2708 /// Properties of a stacking context that are maintained
2709 /// during creation of the scene. These structures are
2710 /// not persisted after the initial scene build.
2711 struct FlattenedStackingContext {
2712 /// Pipeline this stacking context belongs to.
2713 pipeline_id: PipelineId,
2714
2715 /// Filters / mix-blend-mode effects
2716 composite_ops: CompositeOps,
2717
2718 /// If true, visible when backface is visible.
2719 is_backface_visible: bool,
2720
2721 /// Allow subpixel AA for text runs on this stacking context.
2722 /// This is a temporary hack while we don't support subpixel AA
2723 /// on transparent stacking contexts.
2724 allow_subpixel_aa: bool,
2725
2726 /// CSS transform-style property.
2727 transform_style: TransformStyle,
2728
2729 /// If Some(..), this stacking context establishes a new
2730 /// 3d rendering context, and the value is the primitive
2731 // index of the 3d context container.
2732 rendering_context_3d_prim_index: Option<PrimitiveIndex>,
2733 }
2734
2735 #[derive(Debug)]
2736 pub struct ScrollbarInfo(pub ClipScrollNodeIndex, pub LayerRect);
2737