layout: Implement opacity per CSS-COLOR § 3.2.

This adds the infrastructure necessary to support stacking contexts that
are not containing blocks for absolutely-positioned elements. Our
infrastructure did not support that before. This minor revamp actually
ended up simplifying the logic around display list building and
stacking-relative position computation for absolutely-positioned flows,
which was nice.
This commit is contained in:
Patrick Walton 2014-11-18 15:37:53 -08:00
parent 873ca6cadd
commit 1c1c507c03
17 changed files with 421 additions and 146 deletions

View file

@ -146,6 +146,8 @@ pub struct StackingContext {
pub clip_rect: Rect<Au>,
/// The `z-index` for this stacking context.
pub z_index: i32,
/// The opacity of this stacking context.
pub opacity: AzFloat,
}
impl StackingContext {
@ -157,6 +159,7 @@ impl StackingContext {
pub fn new(display_list: Box<DisplayList>,
bounds: Rect<Au>,
z_index: i32,
opacity: AzFloat,
layer: Option<Arc<RenderLayer>>)
-> StackingContext {
StackingContext {
@ -165,6 +168,7 @@ impl StackingContext {
bounds: bounds,
clip_rect: bounds,
z_index: z_index,
opacity: opacity,
}
}
@ -174,82 +178,107 @@ impl StackingContext {
tile_bounds: &Rect<AzFloat>,
current_transform: &Matrix2D<AzFloat>,
current_clip_stack: &mut Vec<Rect<Au>>) {
// Optimize the display list to throw out out-of-bounds display items and so forth.
let display_list = DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
let temporary_draw_target =
render_context.get_or_create_temporary_draw_target(self.opacity);
{
let mut render_subcontext = RenderContext {
draw_target: temporary_draw_target.clone(),
font_ctx: &mut *render_context.font_ctx,
page_rect: render_context.page_rect,
screen_rect: render_context.screen_rect,
..*render_context
};
// Sort positioned children according to z-index.
let mut positioned_children = SmallVec8::new();
for kid in display_list.children.iter() {
positioned_children.push((*kid).clone());
}
positioned_children.as_slice_mut().sort_by(|this, other| this.z_index.cmp(&other.z_index));
// Optimize the display list to throw out out-of-bounds display items and so forth.
let display_list =
DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
// Steps 1 and 2: Borders and background for the root.
for display_item in display_list.background_and_borders.iter() {
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
// Step 3: Positioned descendants with negative z-indices.
for positioned_kid in positioned_children.iter() {
if positioned_kid.z_index >= 0 {
break
// Sort positioned children according to z-index.
let mut positioned_children = SmallVec8::new();
for kid in display_list.children.iter() {
positioned_children.push((*kid).clone());
}
if positioned_kid.layer.is_none() {
let new_transform =
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
as AzFloat,
positioned_kid.bounds.origin.y.to_nearest_px()
as AzFloat);
let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid);
positioned_kid.optimize_and_draw_into_context(render_context,
&new_tile_rect,
&new_transform,
current_clip_stack);
}
}
positioned_children.as_slice_mut()
.sort_by(|this, other| this.z_index.cmp(&other.z_index));
// Step 4: Block backgrounds and borders.
for display_item in display_list.block_backgrounds_and_borders.iter() {
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
// Step 5: Floats.
for display_item in display_list.floats.iter() {
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
// Step 7: Content.
for display_item in display_list.content.iter() {
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
// Steps 8 and 9: Positioned descendants with nonnegative z-indices.
for positioned_kid in positioned_children.iter() {
if positioned_kid.z_index < 0 {
continue
// Steps 1 and 2: Borders and background for the root.
for display_item in display_list.background_and_borders.iter() {
display_item.draw_into_context(&mut render_subcontext,
current_transform,
current_clip_stack)
}
if positioned_kid.layer.is_none() {
let new_transform =
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
as AzFloat,
positioned_kid.bounds.origin.y.to_nearest_px()
as AzFloat);
let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid);
positioned_kid.optimize_and_draw_into_context(render_context,
&new_tile_rect,
&new_transform,
current_clip_stack);
// Step 3: Positioned descendants with negative z-indices.
for positioned_kid in positioned_children.iter() {
if positioned_kid.z_index >= 0 {
break
}
if positioned_kid.layer.is_none() {
let new_transform =
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
as AzFloat,
positioned_kid.bounds.origin.y.to_nearest_px()
as AzFloat);
let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid);
positioned_kid.optimize_and_draw_into_context(&mut render_subcontext,
&new_tile_rect,
&new_transform,
current_clip_stack);
}
}
// Step 4: Block backgrounds and borders.
for display_item in display_list.block_backgrounds_and_borders.iter() {
display_item.draw_into_context(&mut render_subcontext,
current_transform,
current_clip_stack)
}
// Step 5: Floats.
for display_item in display_list.floats.iter() {
display_item.draw_into_context(&mut render_subcontext,
current_transform,
current_clip_stack)
}
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
// Step 7: Content.
for display_item in display_list.content.iter() {
display_item.draw_into_context(&mut render_subcontext,
current_transform,
current_clip_stack)
}
// Steps 8 and 9: Positioned descendants with nonnegative z-indices.
for positioned_kid in positioned_children.iter() {
if positioned_kid.z_index < 0 {
continue
}
if positioned_kid.layer.is_none() {
let new_transform =
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
as AzFloat,
positioned_kid.bounds.origin.y.to_nearest_px()
as AzFloat);
let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid);
positioned_kid.optimize_and_draw_into_context(&mut render_subcontext,
&new_tile_rect,
&new_transform,
current_clip_stack);
}
}
// TODO(pcwalton): Step 10: Outlines.
}
// TODO(pcwalton): Step 10: Outlines.
render_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target,
self.opacity)
}
/// Translate the given tile rect into the coordinate system of a child stacking context.