mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
layout: Rewrite clipping to be per-display-item instead of having
a separate `ClipDisplayItem`. We push down clipping areas during absolute position calculation. This makes display items into a flat list, improving cache locality. It dramatically simplifies the code all around. Because we need to push down clip rects even for absolutely-positioned children of non-absolutely-positioned flows, this patch alters the parallel traversal to compute absolute positions for absolutely-positioned children at the same time it computes absolute positions for other children. This doesn't seem to break anything either in theory (since the overall order remains correct) or in practice. It simplifies the parallel traversal code quite a bit. See the relevant Gecko bug: https://bugzilla.mozilla.org/show_bug.cgi?id=615734
This commit is contained in:
parent
f350879574
commit
bffaad118e
12 changed files with 312 additions and 487 deletions
|
@ -196,93 +196,35 @@ impl StackingContext {
|
|||
};
|
||||
|
||||
for item in list.into_iter() {
|
||||
match item {
|
||||
ClipDisplayItemClass(box ClipDisplayItem {
|
||||
base: base,
|
||||
children: sublist
|
||||
}) => {
|
||||
let sub_stacking_context = StackingContext::new(sublist);
|
||||
stacking_context.merge_with_clip(sub_stacking_context, &base.bounds, base.node)
|
||||
match item.base().level {
|
||||
BackgroundAndBordersStackingLevel => {
|
||||
stacking_context.background_and_borders.push(item)
|
||||
}
|
||||
item => {
|
||||
match item.base().level {
|
||||
BackgroundAndBordersStackingLevel => {
|
||||
stacking_context.background_and_borders.push(item)
|
||||
}
|
||||
BlockBackgroundsAndBordersStackingLevel => {
|
||||
stacking_context.block_backgrounds_and_borders.push(item)
|
||||
}
|
||||
FloatStackingLevel => stacking_context.floats.push(item),
|
||||
ContentStackingLevel => stacking_context.content.push(item),
|
||||
PositionedDescendantStackingLevel(z_index) => {
|
||||
match stacking_context.positioned_descendants
|
||||
.iter_mut()
|
||||
.find(|& &(z, _)| z_index == z) {
|
||||
Some(&(_, ref mut my_list)) => {
|
||||
my_list.push(item);
|
||||
continue
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let mut new_list = DisplayList::new();
|
||||
new_list.list.push(item);
|
||||
stacking_context.positioned_descendants.push((z_index, new_list))
|
||||
BlockBackgroundsAndBordersStackingLevel => {
|
||||
stacking_context.block_backgrounds_and_borders.push(item)
|
||||
}
|
||||
FloatStackingLevel => stacking_context.floats.push(item),
|
||||
ContentStackingLevel => stacking_context.content.push(item),
|
||||
PositionedDescendantStackingLevel(z_index) => {
|
||||
match stacking_context.positioned_descendants
|
||||
.iter_mut()
|
||||
.find(|& &(z, _)| z_index == z) {
|
||||
Some(&(_, ref mut my_list)) => {
|
||||
my_list.push(item);
|
||||
continue
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let mut new_list = DisplayList::new();
|
||||
new_list.list.push(item);
|
||||
stacking_context.positioned_descendants.push((z_index, new_list))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stacking_context
|
||||
}
|
||||
|
||||
/// Merges another stacking context into this one, with the given clipping rectangle and DOM
|
||||
/// node that supplies it.
|
||||
fn merge_with_clip(&mut self,
|
||||
other: StackingContext,
|
||||
clip_rect: &Rect<Au>,
|
||||
clipping_dom_node: OpaqueNode) {
|
||||
let StackingContext {
|
||||
background_and_borders,
|
||||
block_backgrounds_and_borders,
|
||||
floats,
|
||||
content,
|
||||
positioned_descendants: positioned_descendants
|
||||
} = other;
|
||||
|
||||
let push = |destination: &mut DisplayList, source: DisplayList, level| {
|
||||
if !source.is_empty() {
|
||||
let base = BaseDisplayItem::new(*clip_rect, clipping_dom_node, level);
|
||||
destination.push(ClipDisplayItemClass(box ClipDisplayItem::new(base, source)))
|
||||
}
|
||||
};
|
||||
|
||||
push(&mut self.background_and_borders,
|
||||
background_and_borders,
|
||||
BackgroundAndBordersStackingLevel);
|
||||
push(&mut self.block_backgrounds_and_borders,
|
||||
block_backgrounds_and_borders,
|
||||
BlockBackgroundsAndBordersStackingLevel);
|
||||
push(&mut self.floats, floats, FloatStackingLevel);
|
||||
push(&mut self.content, content, ContentStackingLevel);
|
||||
|
||||
for (z_index, list) in positioned_descendants.into_iter() {
|
||||
match self.positioned_descendants
|
||||
.iter_mut()
|
||||
.find(|& &(existing_z_index, _)| z_index == existing_z_index) {
|
||||
Some(&(_, ref mut existing_list)) => {
|
||||
push(existing_list, list, PositionedDescendantStackingLevel(z_index));
|
||||
continue
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let mut new_list = DisplayList::new();
|
||||
push(&mut new_list, list, PositionedDescendantStackingLevel(z_index));
|
||||
self.positioned_descendants.push((z_index, new_list));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which level to place backgrounds and borders in.
|
||||
|
@ -342,11 +284,13 @@ impl DisplayList {
|
|||
|
||||
/// Draws the display list into the given render context. The display list must be flattened
|
||||
/// first for correct painting.
|
||||
pub fn draw_into_context(&self, render_context: &mut RenderContext,
|
||||
current_transform: &Matrix2D<AzFloat>) {
|
||||
pub fn draw_into_context(&self,
|
||||
render_context: &mut RenderContext,
|
||||
current_transform: &Matrix2D<AzFloat>,
|
||||
current_clip_rect: &Rect<Au>) {
|
||||
debug!("Beginning display list.");
|
||||
for item in self.list.iter() {
|
||||
item.draw_into_context(render_context, current_transform)
|
||||
item.draw_into_context(render_context, current_transform, current_clip_rect)
|
||||
}
|
||||
debug!("Ending display list.");
|
||||
}
|
||||
|
@ -356,12 +300,6 @@ impl DisplayList {
|
|||
ParentDisplayItemIterator(self.list.iter())
|
||||
}
|
||||
|
||||
/// Returns true if this list is empty and false otherwise.
|
||||
#[inline]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.list.is_empty()
|
||||
}
|
||||
|
||||
/// Flattens a display list into a display list with a single stacking level according to the
|
||||
/// steps in CSS 2.1 § E.2.
|
||||
///
|
||||
|
@ -421,10 +359,6 @@ impl DisplayList {
|
|||
fn set_stacking_level(&mut self, new_level: StackingLevel) {
|
||||
for item in self.list.iter_mut() {
|
||||
item.mut_base().level = new_level;
|
||||
match item.mut_sublist() {
|
||||
None => {}
|
||||
Some(sublist) => sublist.set_stacking_level(new_level),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -437,7 +371,6 @@ pub enum DisplayItem {
|
|||
ImageDisplayItemClass(Box<ImageDisplayItem>),
|
||||
BorderDisplayItemClass(Box<BorderDisplayItem>),
|
||||
LineDisplayItemClass(Box<LineDisplayItem>),
|
||||
ClipDisplayItemClass(Box<ClipDisplayItem>),
|
||||
|
||||
/// A pseudo-display item that exists only so that queries like `ContentBoxQuery` and
|
||||
/// `ContentBoxesQuery` can be answered.
|
||||
|
@ -450,9 +383,7 @@ pub enum DisplayItem {
|
|||
/// Information common to all display items.
|
||||
#[deriving(Clone)]
|
||||
pub struct BaseDisplayItem {
|
||||
/// The boundaries of the display item.
|
||||
///
|
||||
/// TODO: Which coordinate system should this use?
|
||||
/// The boundaries of the display item, in layer coordinates.
|
||||
pub bounds: Rect<Au>,
|
||||
|
||||
/// The originating DOM node.
|
||||
|
@ -460,14 +391,22 @@ pub struct BaseDisplayItem {
|
|||
|
||||
/// The stacking level in which this display item lives.
|
||||
pub level: StackingLevel,
|
||||
|
||||
/// The rectangle to clip to.
|
||||
///
|
||||
/// TODO(pcwalton): Eventually, to handle `border-radius`, this will (at least) need to grow
|
||||
/// the ability to describe rounded rectangles.
|
||||
pub clip_rect: Rect<Au>,
|
||||
}
|
||||
|
||||
impl BaseDisplayItem {
|
||||
pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel) -> BaseDisplayItem {
|
||||
pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel, clip_rect: Rect<Au>)
|
||||
-> BaseDisplayItem {
|
||||
BaseDisplayItem {
|
||||
bounds: bounds,
|
||||
node: node,
|
||||
level: level,
|
||||
clip_rect: clip_rect,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -544,25 +483,6 @@ pub struct LineDisplayItem {
|
|||
pub style: border_style::T
|
||||
}
|
||||
|
||||
/// Clips a list of child display items to this display item's boundaries.
|
||||
#[deriving(Clone)]
|
||||
pub struct ClipDisplayItem {
|
||||
/// The base information.
|
||||
pub base: BaseDisplayItem,
|
||||
|
||||
/// The child nodes.
|
||||
pub children: DisplayList,
|
||||
}
|
||||
|
||||
impl ClipDisplayItem {
|
||||
pub fn new(base: BaseDisplayItem, children: DisplayList) -> ClipDisplayItem {
|
||||
ClipDisplayItem {
|
||||
base: base,
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DisplayItemIterator<'a> {
|
||||
EmptyDisplayItemIterator,
|
||||
ParentDisplayItemIterator(dlist::Items<'a,DisplayItem>),
|
||||
|
@ -580,24 +500,24 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
|
|||
|
||||
impl DisplayItem {
|
||||
/// Renders this display item into the given render context.
|
||||
fn draw_into_context(&self, render_context: &mut RenderContext,
|
||||
current_transform: &Matrix2D<AzFloat>) {
|
||||
fn draw_into_context(&self,
|
||||
render_context: &mut RenderContext,
|
||||
current_transform: &Matrix2D<AzFloat>,
|
||||
current_clip_rect: &Rect<Au>) {
|
||||
// This should have been flattened to the content stacking level first.
|
||||
assert!(self.base().level == ContentStackingLevel);
|
||||
|
||||
let clip_rect = &self.base().clip_rect;
|
||||
let need_to_clip = current_clip_rect != clip_rect;
|
||||
if need_to_clip {
|
||||
render_context.draw_push_clip(clip_rect);
|
||||
}
|
||||
|
||||
match *self {
|
||||
SolidColorDisplayItemClass(ref solid_color) => {
|
||||
render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
|
||||
}
|
||||
|
||||
ClipDisplayItemClass(ref clip) => {
|
||||
render_context.draw_push_clip(&clip.base.bounds);
|
||||
for item in clip.children.iter() {
|
||||
(*item).draw_into_context(render_context, current_transform);
|
||||
}
|
||||
render_context.draw_pop_clip();
|
||||
}
|
||||
|
||||
TextDisplayItemClass(ref text) => {
|
||||
debug!("Drawing text at {}.", text.base.bounds);
|
||||
|
||||
|
@ -688,6 +608,10 @@ impl DisplayItem {
|
|||
|
||||
PseudoDisplayItemClass(_) => {}
|
||||
}
|
||||
|
||||
if need_to_clip {
|
||||
render_context.draw_pop_clip();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem {
|
||||
|
@ -697,7 +621,6 @@ impl DisplayItem {
|
|||
ImageDisplayItemClass(ref image_item) => &image_item.base,
|
||||
BorderDisplayItemClass(ref border) => &border.base,
|
||||
LineDisplayItemClass(ref line) => &line.base,
|
||||
ClipDisplayItemClass(ref clip) => &clip.base,
|
||||
PseudoDisplayItemClass(ref base) => &**base,
|
||||
}
|
||||
}
|
||||
|
@ -709,7 +632,6 @@ impl DisplayItem {
|
|||
ImageDisplayItemClass(ref mut image_item) => &mut image_item.base,
|
||||
BorderDisplayItemClass(ref mut border) => &mut border.base,
|
||||
LineDisplayItemClass(ref mut line) => &mut line.base,
|
||||
ClipDisplayItemClass(ref mut clip) => &mut clip.base,
|
||||
PseudoDisplayItemClass(ref mut base) => &mut **base,
|
||||
}
|
||||
}
|
||||
|
@ -718,40 +640,12 @@ impl DisplayItem {
|
|||
self.base().bounds
|
||||
}
|
||||
|
||||
pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> {
|
||||
match *self {
|
||||
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.children.list.iter()),
|
||||
SolidColorDisplayItemClass(..) |
|
||||
TextDisplayItemClass(..) |
|
||||
ImageDisplayItemClass(..) |
|
||||
BorderDisplayItemClass(..) |
|
||||
LineDisplayItemClass(..) |
|
||||
PseudoDisplayItemClass(..) => EmptyDisplayItemIterator,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the sublist contained within this display list item, if any.
|
||||
fn mut_sublist<'a>(&'a mut self) -> Option<&'a mut DisplayList> {
|
||||
match *self {
|
||||
ClipDisplayItemClass(ref mut clip) => Some(&mut clip.children),
|
||||
SolidColorDisplayItemClass(..) |
|
||||
TextDisplayItemClass(..) |
|
||||
ImageDisplayItemClass(..) |
|
||||
BorderDisplayItemClass(..) |
|
||||
LineDisplayItemClass(..) |
|
||||
PseudoDisplayItemClass(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_with_level(&self, level: uint) {
|
||||
let mut indent = String::new();
|
||||
for _ in range(0, level) {
|
||||
indent.push_str("| ")
|
||||
}
|
||||
debug!("{}+ {}", indent, self);
|
||||
for child in self.children() {
|
||||
child.debug_with_level(level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -764,7 +658,6 @@ impl fmt::Show for DisplayItem {
|
|||
ImageDisplayItemClass(_) => "Image",
|
||||
BorderDisplayItemClass(_) => "Border",
|
||||
LineDisplayItemClass(_) => "Line",
|
||||
ClipDisplayItemClass(_) => "Clip",
|
||||
PseudoDisplayItemClass(_) => "Pseudo",
|
||||
},
|
||||
self.base().bounds,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue