Auto merge of #8140 - mrobinson:canvas, r=pcwalton

Integrate Canvas into the DisplayList

Canvas is currently given a layer at the stacking context level.
Instead it's DisplayItem should be given a layer directly. This fixes
painting order issues where canvases are painted on top of other
positioned content that is later in tree order. It always simplifies
the code a bit.

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8140)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2015-10-23 17:17:12 -05:00
commit 22a6884a67
5 changed files with 83 additions and 74 deletions

View file

@ -245,6 +245,7 @@ pub trait FragmentDisplayListBuilding {
/// A helper method that `build_display_list` calls to create per-fragment-type display items.
fn build_fragment_type_specific_display_items(&mut self,
display_list: &mut DisplayList,
layout_context: &LayoutContext,
stacking_relative_border_box: &Rect<Au>,
clip: &ClippingRegion);
@ -252,7 +253,6 @@ pub trait FragmentDisplayListBuilding {
fn create_stacking_context(&self,
base_flow: &BaseFlow,
display_list: Box<DisplayList>,
layout_context: &LayoutContext,
scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode)
-> Arc<StackingContext>;
@ -1042,6 +1042,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Create special per-fragment-type display items.
self.build_fragment_type_specific_display_items(display_list,
layout_context,
&stacking_relative_border_box,
&clip);
@ -1054,6 +1055,7 @@ impl FragmentDisplayListBuilding for Fragment {
fn build_fragment_type_specific_display_items(&mut self,
display_list: &mut DisplayList,
layout_context: &LayoutContext,
stacking_relative_border_box: &Rect<Au>,
clip: &ClippingRegion) {
// Compute the context box position relative to the parent stacking context.
@ -1154,16 +1156,25 @@ impl FragmentDisplayListBuilding for Fragment {
.computed_inline_size.map_or(0, |w| w.to_px() as usize);
let height = canvas_fragment_info.replaced_image_fragment_info
.computed_block_size.map_or(0, |h| h.to_px() as usize);
if width > 0 && height > 0 {
let (sender, receiver) = ipc::channel::<IpcSharedMemory>().unwrap();
let layer_id = self.layer_id();
let canvas_data = match canvas_fragment_info.ipc_renderer {
Some(ref ipc_renderer) => {
ipc_renderer.lock().unwrap().send(CanvasMsg::FromLayout(
let ipc_renderer = ipc_renderer.lock().unwrap();
ipc_renderer.send(CanvasMsg::FromLayout(
FromLayoutMsg::SendPixelContents(sender))).unwrap();
receiver.recv().unwrap()
let data = receiver.recv().unwrap();
// Propagate the layer and the renderer to the paint task.
layout_context.shared.canvas_layers_sender.send(
(layer_id, (*ipc_renderer).clone())).unwrap();
data
},
None => IpcSharedMemory::from_byte(0xFFu8, width * height * 4),
};
display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem {
let display_item = DisplayItem::ImageClass(box ImageDisplayItem {
base: BaseDisplayItem::new(stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
@ -1177,7 +1188,16 @@ impl FragmentDisplayListBuilding for Fragment {
}),
stretch_size: stacking_relative_content_box.size,
image_rendering: image_rendering::T::Auto,
});
display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem {
item: display_item,
layer_id: layer_id
}));
display_list.layer_info.push_back(
LayerInfo::new(layer_id, ScrollPolicy::Scrollable, None));
}
}
SpecificFragmentInfo::UnscannedText(_) => {
panic!("Shouldn't see unscanned fragments here.")
@ -1191,7 +1211,6 @@ impl FragmentDisplayListBuilding for Fragment {
fn create_stacking_context(&self,
base_flow: &BaseFlow,
display_list: Box<DisplayList>,
layout_context: &LayoutContext,
scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode)
-> Arc<StackingContext> {
@ -1305,32 +1324,16 @@ impl FragmentDisplayListBuilding for Fragment {
filters.push(Filter::Opacity(effects.opacity))
}
let canvas = match self.specific {
SpecificFragmentInfo::Canvas(_) => true,
_ => false
};
// There are three situations that need layers: when the fragment has the HAS_LAYER
// flag, when this is a canvas or iframe fragment, and when we are building a layer
// tree for overflow scrolling.
// There are two situations that need layers: when the fragment has the HAS_LAYER
// flag and when we are building a layer tree for overflow scrolling.
let layer_info = if mode == StackingContextCreationMode::InnerScrollWrapper {
Some(LayerInfo::new(self.layer_id_for_overflow_scroll(), scroll_policy, None))
} else if self.flags.contains(HAS_LAYER) || canvas {
} else if self.flags.contains(HAS_LAYER) {
Some(LayerInfo::new(self.layer_id(), scroll_policy, None))
} else {
None
};
// If it's a canvas we must propagate the layer and the renderer to the paint task.
if let SpecificFragmentInfo::Canvas(ref fragment_info) = self.specific {
let layer_id = layer_info.unwrap().layer_id;
if let Some(ref ipc_renderer) = fragment_info.ipc_renderer {
layout_context.shared
.canvas_layers_sender
.send((layer_id, (*ipc_renderer.lock().unwrap()).clone())).unwrap();
}
}
let scrolls_overflow_area = mode == StackingContextCreationMode::OuterScrollWrapper;
let transform_style = self.style().get_used_transform_style();
let establishes_3d_context = scrolls_overflow_area ||
@ -1603,7 +1606,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
self.fragment.create_stacking_context(
&self.base,
display_list,
layout_context,
scroll_policy,
StackingContextCreationMode::Normal))
} else {
@ -1685,13 +1687,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
outer_display_list.children.push_back(self.fragment.create_stacking_context(
&self.base,
display_list,
layout_context,
scroll_policy,
StackingContextCreationMode::InnerScrollWrapper));
self.fragment.create_stacking_context(
&self.base,
outer_display_list,
layout_context,
scroll_policy,
StackingContextCreationMode::OuterScrollWrapper)
}
@ -1699,7 +1699,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
self.fragment.create_stacking_context(
&self.base,
display_list,
layout_context,
scroll_policy,
StackingContextCreationMode::Normal)
}
@ -1723,7 +1722,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
DisplayListBuildingResult::StackingContext(
self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
ScrollPolicy::Scrollable,
StackingContextCreationMode::Normal))
} else {
@ -1830,7 +1828,6 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
self.fragments.fragments[0].create_stacking_context(
&self.base,
display_list,
layout_context,
ScrollPolicy::Scrollable,
StackingContextCreationMode::Normal))
} else {

View file

@ -2106,16 +2106,6 @@ impl Fragment {
stacking_relative_border_box.size.height - border_padding.vertical()))
}
/// Returns true if this fragment unconditionally layerizes.
pub fn needs_layered_stacking_context(&self) -> bool {
// Canvas and iframes always layerize, as an special case
// FIXME(pcwalton): Don't unconditionally form stacking contexts for each canvas.
match self.specific {
SpecificFragmentInfo::Canvas(_) => true,
_ => false,
}
}
/// Returns true if this fragment establishes a new stacking context and false otherwise.
pub fn establishes_stacking_context(&self) -> bool {
if self.flags.contains(HAS_LAYER) {
@ -2140,10 +2130,6 @@ impl Fragment {
transform_style::T::auto => {}
}
if self.needs_layered_stacking_context() {
return true
}
// FIXME(pcwalton): Don't unconditionally form stacking contexts for `overflow_x: scroll`
// and `overflow_y: scroll`. This needs multiple layers per stacking context.
match (self.style().get_box().position,

View file

@ -10,7 +10,7 @@ use context::LayoutContext;
use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding};
use euclid::{Point2D, Rect, Size2D};
use floats::{FloatKind, Floats, PlacementInfo};
use flow::{EarlyAbsolutePositionInfo, LAYERS_NEEDED_FOR_DESCENDANTS, MutableFlowUtils, OpaqueFlow};
use flow::{EarlyAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED};
use flow_ref;
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
@ -1467,7 +1467,6 @@ impl Flow for InlineFlow {
// Now, go through each line and lay out the fragments inside.
let mut line_distance_from_flow_block_start = Au(0);
let mut layers_needed_for_descendants = false;
let line_count = self.lines.len();
for line_index in 0..line_count {
let line = &mut self.lines[line_index];
@ -1496,10 +1495,6 @@ impl Flow for InlineFlow {
for fragment_index in line.range.each_index() {
let fragment = &mut self.fragments.fragments[fragment_index.to_usize()];
if fragment.needs_layered_stacking_context() && !fragment.is_positioned() {
layers_needed_for_descendants = true
}
let InlineMetrics {
mut block_size_above_baseline,
mut depth_below_baseline,
@ -1596,10 +1591,6 @@ impl Flow for InlineFlow {
kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id);
}
// Mark ourselves for layerization if that will be necessary to paint in the proper
// order (CSS 2.1, Appendix E).
self.base.flags.set(LAYERS_NEEDED_FOR_DESCENDANTS, layers_needed_for_descendants);
if self.contains_positioned_fragments() {
// Assign block-sizes for all flows in this absolute flow tree.
// This is preorder because the block-size of an absolute flow may depend on

View file

@ -43,5 +43,30 @@
<div class="gray box" style="position: relative; left: 20px; top: -30px;"> </div>
</div>
<!-- The canvas should be painted in tree order since it is not positioned and
the following div is. -->
<div class="test grayest box">
<canvas class="box" id="canvas1" style="display: block; padding-left: 10px; padding-top: 10px;" width="50" height="50"></canvas>
<div class="gray box" style="position: relative; left: 20px; top: -40px;"> </div>
</div>
<!-- The canvas should be painted in tree order since both it and the inner div are
not positioned elements. -->
<div class="test grayest box">
<canvas class="box" id="canvas2" style="display: block; position: relative; left: 10px; top: 10px;" width="50" height="50"></canvas>
<div class="gray box" style="position: relative; left: 20px; top: -30px;"> </div>
</div>
<script>
function paintCanvas(id) {
var canvas = document.getElementById(id);
var context = canvas.getContext("2d");
context.fillStyle = "rgb(80, 80, 80)";
context.fillRect(0, 0, 100, 100);
}
paintCanvas("canvas1");
paintCanvas("canvas2");
</script>
</body>
</html>

View file

@ -34,5 +34,15 @@
<div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div>
<div class="gray box" style="margin-left: 20px; margin-top: -40px;"></div>
</div>
<div class="test grayest box">
<div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div>
<div class="gray box" style="margin-left: 20px; margin-top: -40px;"></div>
</div>
<div class="test grayest box">
<div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div>
<div class="gray box" style="margin-left: 20px; margin-top: -40px;"></div>
</div>
</body>
</html>