diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index c7053472b69..1fcbafbea47 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -26,7 +26,7 @@ use libc::uintptr_t; use msg::compositor_msg::{LayerId, LayerKind, ScrollPolicy, SubpageLayerInfo}; use net_traits::image::base::Image; use paint_context::PaintContext; -use paint_task::PaintLayer; +use paint_task::{PaintLayerContents, PaintLayer}; use self::DisplayItem::*; use self::DisplayItemIterator::*; use smallvec::SmallVec; @@ -254,21 +254,38 @@ impl DisplayList { print_display_list_section(print_tree, &self.outlines, "Outlines"); - for stacking_context in &self.children { - stacking_context.print_with_tree(print_tree); + if !self.children.is_empty() { + print_tree.new_level("Stacking Contexts".to_owned()); + for stacking_context in &self.children { + stacking_context.print_with_tree(print_tree); + } + print_tree.end_level(); } - for paint_layer in &self.layered_children { - paint_layer.stacking_context.print_with_tree(print_tree); + if !self.layered_children.is_empty() { + print_tree.new_level("Layers".to_owned()); + for paint_layer in &self.layered_children { + match paint_layer.contents { + PaintLayerContents::StackingContext(ref stacking_context) => + stacking_context.print_with_tree(print_tree), + PaintLayerContents::DisplayList(ref display_list) => { + print_tree.new_level(format!("DisplayList Layer with bounds {:?}:", + display_list.calculate_bounding_rect())); + display_list.print_with_tree(print_tree); + print_tree.end_level(); + } + } + } + print_tree.end_level(); } } /// Draws the DisplayList in stacking context order according to the steps in CSS 2.1 ยง E.2. - fn draw_into_context(&self, - draw_target: &DrawTarget, - paint_context: &mut PaintContext, - transform: &Matrix4, - clip_rect: Option<&Rect>) { + pub fn draw_into_context(&self, + draw_target: &DrawTarget, + paint_context: &mut PaintContext, + transform: &Matrix4, + clip_rect: Option<&Rect>) { let mut paint_subcontext = PaintContext { draw_target: draw_target.clone(), font_context: &mut *paint_context.font_context, @@ -422,9 +439,15 @@ impl DisplayList { } } - // Layers are positioned on top of this layer should get a shot at the hit test first. + // Layers that are positioned on top of this layer should get a shot at the hit test first. for layer in self.layered_children.iter().rev() { - layer.stacking_context.hit_test(point, result, topmost_only); + match layer.contents { + PaintLayerContents::StackingContext(ref stacking_context) => + stacking_context.hit_test(point, result, topmost_only), + PaintLayerContents::DisplayList(ref display_list) => + display_list.hit_test(point, result, topmost_only), + } + if topmost_only && !result.is_empty() { return } @@ -484,6 +507,51 @@ impl DisplayList { self.background_and_borders.iter().rev()) } + + /// Returns the PaintLayer in the given DisplayList with a specific layer ID. + pub fn find_layer_with_layer_id(&self, layer_id: LayerId) -> Option> { + for kid in &self.layered_children { + if let Some(paint_layer) = PaintLayer::find_layer_with_layer_id(&kid, layer_id) { + return Some(paint_layer); + } + } + + for kid in &self.children { + if let Some(paint_layer) = kid.display_list.find_layer_with_layer_id(layer_id) { + return Some(paint_layer); + } + } + + None + } + + /// Calculate the union of all the bounds of all of the items in this display list. + /// This is an expensive operation, so it shouldn't be done unless absolutely necessary + /// and, if possible, the result should be cached. + pub fn calculate_bounding_rect(&self) -> Rect { + fn union_all_items(list: &LinkedList, mut bounds: Rect) -> Rect { + for item in list { + bounds = bounds.union(&item.base().bounds); + } + bounds + }; + + let mut bounds = Rect::zero(); + bounds = union_all_items(&self.background_and_borders, bounds); + bounds = union_all_items(&self.block_backgrounds_and_borders, bounds); + bounds = union_all_items(&self.floats, bounds); + bounds = union_all_items(&self.content, bounds); + bounds = union_all_items(&self.positioned_content, bounds); + bounds = union_all_items(&self.outlines, bounds); + + for stacking_context in &self.children { + bounds = bounds.union(&Rect::new( + stacking_context.overflow.origin + stacking_context.bounds.origin, + stacking_context.overflow.size)); + } + + bounds + } } #[derive(HeapSizeOf, Deserialize, Serialize)] @@ -685,7 +753,7 @@ impl StackingContext { } struct StackingContextLayerCreator { - display_list_for_next_layer: Option>, + display_list_for_next_layer: Option, next_layer_info: Option, } @@ -726,12 +794,8 @@ impl StackingContextLayerCreator { fn finish_building_current_layer(&mut self, stacking_context: &mut StackingContext) { if let Some(display_list) = self.display_list_for_next_layer.take() { let layer_info = self.next_layer_info.take().unwrap(); - let child_stacking_context = - Arc::new(stacking_context.create_layered_child(layer_info.clone(), display_list)); stacking_context.display_list.layered_children.push_back( - Arc::new(PaintLayer::new(layer_info, - color::transparent(), - child_stacking_context))); + Arc::new(PaintLayer::new_with_display_list(layer_info, display_list))); } } @@ -766,7 +830,7 @@ impl StackingContextLayerCreator { } if self.display_list_for_next_layer.is_none() { - self.display_list_for_next_layer = Some(box DisplayList::new()); + self.display_list_for_next_layer = Some(DisplayList::new()); } if let Some(ref mut display_list) = self.display_list_for_next_layer { display_list.children.push_back(stacking_context); @@ -774,25 +838,6 @@ impl StackingContextLayerCreator { } } -/// Returns the stacking context in the given tree of stacking contexts with a specific layer ID. -pub fn find_layer_with_layer_id(this: &Arc, - layer_id: LayerId) - -> Option> { - for kid in &this.display_list.layered_children { - if let Some(paint_layer) = PaintLayer::find_layer_with_layer_id(&kid, layer_id) { - return Some(paint_layer); - } - } - - for kid in &this.display_list.children { - if let Some(paint_layer) = find_layer_with_layer_id(kid, layer_id) { - return Some(paint_layer); - } - } - - None -} - /// One drawing command in the list. #[derive(Clone, Deserialize, HeapSizeOf, Serialize)] pub enum DisplayItem { diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs index 44a84f825b1..0c58155ab2d 100644 --- a/components/gfx/paint_task.rs +++ b/components/gfx/paint_task.rs @@ -8,13 +8,14 @@ use app_units::Au; use azure::AzFloat; use azure::azure_hl::{BackendType, Color, DrawTarget, SurfaceFormat}; use canvas_traits::CanvasMsg; -use display_list::{self, LayerInfo, StackingContext}; +use display_list::{DisplayList, LayerInfo, StackingContext}; use euclid::Matrix4; use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; use font_cache_task::FontCacheTask; use font_context::FontContext; +use gfx_traits::color; use ipc_channel::ipc::IpcSender; use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet}; use layers::platform::surface::{NativeDisplay, NativeSurface}; @@ -40,6 +41,12 @@ use util::task::spawn_named; use util::task::spawn_named_with_send_on_failure; use util::task_state; +#[derive(Clone, Deserialize, Serialize, HeapSizeOf)] +pub enum PaintLayerContents { + StackingContext(Arc), + DisplayList(Arc), +} + /// Information about a hardware graphics layer that layout sends to the painting task. #[derive(Clone, Deserialize, Serialize, HeapSizeOf)] pub struct PaintLayer { @@ -47,8 +54,10 @@ pub struct PaintLayer { pub id: LayerId, /// The color of the background in this layer. Used for unpainted content. pub background_color: Color, - /// The stacking context that represents the content of this layer. - pub stacking_context: Arc, + /// The content of this layer, which is either a stacking context or a display list. + pub contents: PaintLayerContents, + /// The layer's boundaries in the parent layer's coordinate system. + pub bounds: Rect, /// The scrolling policy of this layer. pub scroll_policy: ScrollPolicy, /// The subpage that this layer represents, if there is one. @@ -56,29 +65,33 @@ pub struct PaintLayer { } impl PaintLayer { - /// Creates a new `PaintLayer`. - pub fn new(layer_info: LayerInfo, - background_color: Color, - stacking_context: Arc) - -> PaintLayer { - PaintLayer { - id: layer_info.layer_id, - background_color: background_color, - stacking_context: stacking_context, - scroll_policy: layer_info.scroll_policy, - subpage_layer_info: layer_info.subpage_layer_info, - } - } - /// Creates a new `PaintLayer` with a stacking context. pub fn new_with_stacking_context(layer_info: LayerInfo, stacking_context: Arc, background_color: Color) -> PaintLayer { - PaintLayer { + let bounds = Rect::new(stacking_context.bounds.origin + stacking_context.overflow.origin, + stacking_context.overflow.size); + PaintLayer { id: layer_info.layer_id, background_color: background_color, - stacking_context: stacking_context, + contents: PaintLayerContents::StackingContext(stacking_context), + bounds: bounds, + scroll_policy: layer_info.scroll_policy, + subpage_layer_info: layer_info.subpage_layer_info, + } + } + + /// Creates a new `PaintLayer` with a display list. + pub fn new_with_display_list(layer_info: LayerInfo, + display_list: DisplayList) + -> PaintLayer { + let bounds = display_list.calculate_bounding_rect(); + PaintLayer { + id: layer_info.layer_id, + background_color: color::transparent(), + contents: PaintLayerContents::DisplayList(Arc::new(display_list)), + bounds: bounds, scroll_policy: layer_info.scroll_policy, subpage_layer_info: layer_info.subpage_layer_info, } @@ -91,7 +104,80 @@ impl PaintLayer { return Some(this.clone()); } - display_list::find_layer_with_layer_id(&this.stacking_context, layer_id) + match this.contents { + PaintLayerContents::StackingContext(ref stacking_context) => + stacking_context.display_list.find_layer_with_layer_id(layer_id), + PaintLayerContents::DisplayList(ref display_list) => + display_list.find_layer_with_layer_id(layer_id), + } + } + + fn build_layer_properties(&self, + parent_origin: &Point2D, + transform: &Matrix4, + perspective: &Matrix4, + parent_id: Option) + -> LayerProperties { + let layer_boundaries = Rect::new( + Point2D::new((parent_origin.x + self.bounds.min_x()).to_nearest_px() as f32, + (parent_origin.y + self.bounds.min_y()).to_nearest_px() as f32), + Size2D::new(self.bounds.size.width.to_nearest_px() as f32, + self.bounds.size.height.to_nearest_px() as f32)); + + let (transform, + perspective, + establishes_3d_context, + scrolls_overflow_area) = match self.contents { + PaintLayerContents::StackingContext(ref stacking_context) => { + (transform.mul(&stacking_context.transform), + perspective.mul(&stacking_context.perspective), + stacking_context.establishes_3d_context, + stacking_context.scrolls_overflow_area) + }, + PaintLayerContents::DisplayList(_) => { + (*transform, *perspective, false, false) + } + }; + + LayerProperties { + id: self.id, + parent_id: parent_id, + rect: layer_boundaries, + background_color: self.background_color, + scroll_policy: self.scroll_policy, + transform: transform, + perspective: perspective, + establishes_3d_context: establishes_3d_context, + scrolls_overflow_area: scrolls_overflow_area, + subpage_layer_info: self.subpage_layer_info, + } + } + + // The origin for child layers might be somewhere other than the layer origin, + // since layer boundaries are expanded to include overflow. + pub fn origin_for_child_layers(&self) -> Point2D { + match self.contents { + PaintLayerContents::StackingContext(ref stacking_context) => + -stacking_context.overflow.origin, + PaintLayerContents::DisplayList(_) => Point2D::zero(), + } + } + + pub fn display_list_origin(&self) -> Point2D { + // The layer's bounds start at the overflow origin, but display items are + // positioned relative to the stacking context counds, so we need to + // offset by the overflow rect (which will be in the coordinate system of + // the stacking context bounds). + match self.contents { + PaintLayerContents::StackingContext(ref stacking_context) => { + Point2D::new(stacking_context.overflow.origin.x.to_f32_px(), + stacking_context.overflow.origin.y.to_f32_px()) + + }, + PaintLayerContents::DisplayList(_) => { + Point2D::new(self.bounds.origin.x.to_f32_px(), self.bounds.origin.y.to_f32_px()) + } + } } } @@ -372,59 +458,37 @@ impl PaintTask where C: PaintListener + Send + 'static { fn build_from_paint_layer(properties: &mut Vec, paint_layer: &Arc, - page_position: &Point2D, + parent_origin: &Point2D, transform: &Matrix4, perspective: &Matrix4, parent_id: Option) { - let transform = transform.mul(&paint_layer.stacking_context.transform); - let perspective = perspective.mul(&paint_layer.stacking_context.perspective); - let overflow_size = Size2D::new( - paint_layer.stacking_context.overflow.size.width.to_nearest_px() as f32, - paint_layer.stacking_context.overflow.size.height.to_nearest_px() as f32); + properties.push(paint_layer.build_layer_properties(parent_origin, + transform, + perspective, + parent_id)); - // Layers start at the top left of their overflow rect, as far as the info - // we give to the compositor is concerned. - let overflow_relative_page_position = *page_position + - paint_layer.stacking_context.bounds.origin + - paint_layer.stacking_context.overflow.origin; - let layer_position = Rect::new( - Point2D::new(overflow_relative_page_position.x.to_nearest_px() as f32, - overflow_relative_page_position.y.to_nearest_px() as f32), - overflow_size); - - properties.push(LayerProperties { - id: paint_layer.id, - parent_id: parent_id, - rect: layer_position, - background_color: paint_layer.background_color, - scroll_policy: paint_layer.scroll_policy, - transform: transform, - perspective: perspective, - establishes_3d_context: paint_layer.stacking_context.establishes_3d_context, - scrolls_overflow_area: paint_layer.stacking_context.scrolls_overflow_area, - subpage_layer_info: paint_layer.subpage_layer_info, - }); - - // When there is a new layer, the transforms and origin are handled by the compositor, - // so the new transform and perspective matrices are just the identity. - continue_walking_stacking_context(properties, - &paint_layer.stacking_context, - &-paint_layer.stacking_context.overflow.origin, - &Matrix4::identity(), - &Matrix4::identity(), - Some(paint_layer.id)); + if let PaintLayerContents::StackingContext(ref context) = paint_layer.contents { + // When there is a new layer, the transforms and origin are handled by the compositor, + // so the new transform and perspective matrices are just the identity. + continue_walking_stacking_context(properties, + &context, + &paint_layer.origin_for_child_layers(), + &Matrix4::identity(), + &Matrix4::identity(), + Some(paint_layer.id)); + } } fn build_from_stacking_context(properties: &mut Vec, stacking_context: &Arc, - page_position: &Point2D, + parent_origin: &Point2D, transform: &Matrix4, perspective: &Matrix4, parent_id: Option) { continue_walking_stacking_context(properties, stacking_context, - &(stacking_context.bounds.origin + *page_position), + &(stacking_context.bounds.origin + *parent_origin), &transform.mul(&stacking_context.transform), &perspective.mul(&stacking_context.perspective), parent_id); @@ -432,14 +496,14 @@ impl PaintTask where C: PaintListener + Send + 'static { fn continue_walking_stacking_context(properties: &mut Vec, stacking_context: &Arc, - page_position: &Point2D, + parent_origin: &Point2D, transform: &Matrix4, perspective: &Matrix4, parent_id: Option) { for kid in stacking_context.display_list.children.iter() { build_from_stacking_context(properties, &kid, - &page_position, + &parent_origin, &transform, &perspective, parent_id) @@ -448,7 +512,7 @@ impl PaintTask where C: PaintListener + Send + 'static { for kid in stacking_context.display_list.layered_children.iter() { build_from_paint_layer(properties, &kid, - &page_position, + &parent_origin, &transform, &perspective, parent_id) @@ -605,7 +669,6 @@ impl WorkerThread { scale: f32, layer_kind: LayerKind) -> Box { - let stacking_context = &paint_layer.stacking_context; let size = Size2D::new(tile.screen_rect.size.width as i32, tile.screen_rect.size.height as i32); let mut buffer = self.create_layer_buffer(&mut tile, scale); @@ -623,15 +686,10 @@ impl WorkerThread { layer_kind: layer_kind, }; - // Apply a translation to start at the boundaries of the stacking context, since the - // layer's origin starts at its overflow rect's origin. - let tile_bounds = tile.page_rect.translate( - &Point2D::new(stacking_context.overflow.origin.x.to_f32_px(), - stacking_context.overflow.origin.y.to_f32_px())); - // Apply the translation to paint the tile we want. let matrix = Matrix4::identity(); let matrix = matrix.scale(scale as AzFloat, scale as AzFloat, 1.0); + let tile_bounds = tile.page_rect.translate(&paint_layer.display_list_origin()); let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat, -tile_bounds.origin.y as AzFloat, 0.0); @@ -644,11 +702,24 @@ impl WorkerThread { None, self.time_profiler_sender.clone(), || { - stacking_context.optimize_and_draw_into_context(&mut paint_context, - &matrix, - None); + match paint_layer.contents { + PaintLayerContents::StackingContext(ref stacking_context) => { + stacking_context.optimize_and_draw_into_context(&mut paint_context, + &matrix, + None); + } + PaintLayerContents::DisplayList(ref display_list) => { + paint_context.remove_transient_clip_if_applicable(); + let draw_target = paint_context.draw_target.clone(); + display_list.draw_into_context(&draw_target, + &mut paint_context, + &matrix, + None); + } + } + paint_context.draw_target.flush(); - }); + }); if opts::get().show_debug_parallel_paint { // Overlay a transparent solid color to identify the thread that