layout: Paint stacking contexts' overflow areas properly.

This was making `box-shadow` not show up in many cases, in particular,
but the effects were not limited to that.
This commit is contained in:
Patrick Walton 2014-12-18 18:59:42 -08:00
parent ba8cf6b0e6
commit 5ea2c6dcfd
30 changed files with 357 additions and 179 deletions

View file

@ -34,7 +34,7 @@ use servo_msg::compositor_msg::LayerId;
use servo_net::image::base::Image;
use servo_util::cursor::Cursor;
use servo_util::dlist as servo_dlist;
use servo_util::geometry::{mod, Au, MAX_RECT, ZERO_POINT, ZERO_RECT};
use servo_util::geometry::{mod, Au, MAX_RECT, ZERO_RECT};
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec8};
use std::fmt;
@ -160,9 +160,8 @@ pub struct StackingContext {
pub layer: Option<Arc<PaintLayer>>,
/// The position and size of this stacking context.
pub bounds: Rect<Au>,
/// The clipping rect for this stacking context, in the coordinate system of the *parent*
/// stacking context.
pub clip_rect: Rect<Au>,
/// The overflow rect for this stacking context in its coordinate system.
pub overflow: Rect<Au>,
/// The `z-index` for this stacking context.
pub z_index: i32,
/// The opacity of this stacking context.
@ -171,12 +170,10 @@ pub struct StackingContext {
impl StackingContext {
/// Creates a new stacking context.
///
/// TODO(pcwalton): Stacking contexts should not always be clipped to their bounds, to handle
/// overflow properly.
#[inline]
pub fn new(display_list: Box<DisplayList>,
bounds: Rect<Au>,
bounds: &Rect<Au>,
overflow: &Rect<Au>,
z_index: i32,
opacity: AzFloat,
layer: Option<Arc<PaintLayer>>)
@ -184,8 +181,8 @@ impl StackingContext {
StackingContext {
display_list: display_list,
layer: layer,
bounds: bounds,
clip_rect: Rect(ZERO_POINT, bounds.size),
bounds: *bounds,
overflow: *overflow,
z_index: z_index,
opacity: opacity,
}
@ -196,7 +193,7 @@ impl StackingContext {
paint_context: &mut PaintContext,
tile_bounds: &Rect<AzFloat>,
transform: &Matrix2D<AzFloat>,
clip_rect: Option<Rect<Au>>) {
clip_rect: Option<&Rect<Au>>) {
let temporary_draw_target =
paint_context.get_or_create_temporary_draw_target(self.opacity);
{
@ -205,7 +202,7 @@ impl StackingContext {
font_ctx: &mut *paint_context.font_ctx,
page_rect: paint_context.page_rect,
screen_rect: paint_context.screen_rect,
clip_rect: clip_rect,
clip_rect: clip_rect.map(|clip_rect| *clip_rect),
transient_clip: None,
};
@ -252,7 +249,7 @@ impl StackingContext {
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
&new_tile_rect,
&new_transform,
Some(positioned_kid.clip_rect))
Some(&positioned_kid.overflow))
}
}
@ -295,7 +292,7 @@ impl StackingContext {
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
&new_tile_rect,
&new_transform,
Some(positioned_kid.clip_rect))
Some(&positioned_kid.overflow))
}
}
@ -329,11 +326,18 @@ impl StackingContext {
}
};
let child_stacking_context_bounds = child_stacking_context.bounds.to_azure_rect();
let tile_subrect = tile_bounds.intersection(&child_stacking_context_bounds)
// Translate the child's overflow region into our coordinate system.
let child_stacking_context_overflow =
child_stacking_context.overflow.translate(&child_stacking_context.bounds.origin)
.to_azure_rect();
// Intersect that with the current tile boundaries to find the tile boundaries that the
// child covers.
let tile_subrect = tile_bounds.intersection(&child_stacking_context_overflow)
.unwrap_or(ZERO_AZURE_RECT);
let offset = tile_subrect.origin - child_stacking_context_bounds.origin;
Rect(offset, tile_subrect.size)
// Translate the resulting rect into the child's coordinate system.
tile_subrect.translate(&-child_stacking_context.bounds.to_azure_rect().origin)
}
/// Places all nodes containing the point of interest into `result`, topmost first. If

View file

@ -59,7 +59,8 @@ impl DisplayListOptimizer {
mut stacking_contexts: I)
where I: Iterator<&'a Arc<StackingContext>> {
for stacking_context in stacking_contexts {
if self.visible_rect.intersects(&stacking_context.bounds) {
let overflow = stacking_context.overflow.translate(&stacking_context.bounds.origin);
if self.visible_rect.intersects(&overflow) {
result_list.push_back((*stacking_context).clone())
}
}

View file

@ -148,20 +148,21 @@ fn initialize_layers<C>(compositor: &mut C,
stacking_context: &StackingContext,
page_position: &Point2D<Au>) {
let page_position = stacking_context.bounds.origin + *page_position;
match stacking_context.layer {
None => {}
Some(ref paint_layer) => {
metadata.push(LayerMetadata {
id: paint_layer.id,
position:
Rect(Point2D(page_position.x.to_nearest_px() as uint,
page_position.y.to_nearest_px() as uint),
Size2D(stacking_context.bounds.size.width.to_nearest_px() as uint,
stacking_context.bounds.size.height.to_nearest_px() as uint)),
background_color: paint_layer.background_color,
scroll_policy: paint_layer.scroll_policy,
})
}
if let Some(ref paint_layer) = stacking_context.layer {
// 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 + stacking_context.overflow.origin;
let layer_position =
Rect(Point2D(overflow_relative_page_position.x.to_nearest_px() as i32,
overflow_relative_page_position.y.to_nearest_px() as i32),
Size2D(stacking_context.overflow.size.width.to_nearest_px() as i32,
stacking_context.overflow.size.height.to_nearest_px() as i32));
metadata.push(LayerMetadata {
id: paint_layer.id,
position: layer_position,
background_color: paint_layer.background_color,
scroll_policy: paint_layer.scroll_policy,
})
}
for kid in stacking_context.display_list.children.iter() {
@ -384,15 +385,14 @@ impl<C> PaintTask<C> where C: PaintListener + Send {
layer_id: LayerId) {
profile(TimeProfilerCategory::Painting, None, self.time_profiler_chan.clone(), || {
// Bail out if there is no appropriate stacking context.
let stacking_context = match self.root_stacking_context {
Some(ref stacking_context) => {
match display_list::find_stacking_context_with_layer_id(stacking_context,
layer_id) {
Some(stacking_context) => stacking_context,
None => return,
}
let stacking_context = if let Some(ref stacking_context) = self.root_stacking_context {
match display_list::find_stacking_context_with_layer_id(stacking_context,
layer_id) {
Some(stacking_context) => stacking_context,
None => return,
}
None => return,
} else {
return
};
// Divide up the layer into tiles and distribute them to workers via a simple round-
@ -547,8 +547,13 @@ impl WorkerThread {
transient_clip: None,
};
// 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(stacking_context.overflow.origin.x.to_subpx() as AzFloat,
stacking_context.overflow.origin.y.to_subpx() as AzFloat));
// Apply the translation to paint the tile we want.
let tile_bounds = tile.page_rect;
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat,
@ -561,7 +566,7 @@ impl WorkerThread {
profile(TimeProfilerCategory::PaintingPerTile, None,
self.time_profiler_sender.clone(), || {
stacking_context.optimize_and_draw_into_context(&mut paint_context,
&tile.page_rect,
&tile_bounds,
&matrix,
None);
paint_context.draw_target.flush();