mirror of
https://github.com/servo/servo.git
synced 2025-07-16 11:53:39 +01:00
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:
commit
22a6884a67
5 changed files with 83 additions and 74 deletions
|
@ -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,30 +1156,48 @@ 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);
|
||||
let (sender, receiver) = ipc::channel::<IpcSharedMemory>().unwrap();
|
||||
let canvas_data = match canvas_fragment_info.ipc_renderer {
|
||||
Some(ref ipc_renderer) => {
|
||||
ipc_renderer.lock().unwrap().send(CanvasMsg::FromLayout(
|
||||
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) => {
|
||||
let ipc_renderer = ipc_renderer.lock().unwrap();
|
||||
ipc_renderer.send(CanvasMsg::FromLayout(
|
||||
FromLayoutMsg::SendPixelContents(sender))).unwrap();
|
||||
receiver.recv().unwrap()
|
||||
},
|
||||
None => IpcSharedMemory::from_byte(0xFFu8, width * height * 4),
|
||||
};
|
||||
display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(stacking_relative_content_box,
|
||||
DisplayItemMetadata::new(self.node,
|
||||
&*self.style,
|
||||
Cursor::DefaultCursor),
|
||||
(*clip).clone()),
|
||||
image: Arc::new(Image {
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
format: PixelFormat::RGBA8,
|
||||
bytes: canvas_data,
|
||||
}),
|
||||
stretch_size: stacking_relative_content_box.size,
|
||||
image_rendering: image_rendering::T::Auto,
|
||||
}));
|
||||
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),
|
||||
};
|
||||
let display_item = DisplayItem::ImageClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(stacking_relative_content_box,
|
||||
DisplayItemMetadata::new(self.node,
|
||||
&*self.style,
|
||||
Cursor::DefaultCursor),
|
||||
(*clip).clone()),
|
||||
image: Arc::new(Image {
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
format: PixelFormat::RGBA8,
|
||||
bytes: canvas_data,
|
||||
}),
|
||||
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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue