Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

layout: Implement `opacity` per CSS-COLOR § 3.2. #4036

Merged
merged 1 commit into from Dec 3, 2014
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

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.
  • Loading branch information
pcwalton committed Dec 3, 2014
commit 1c1c507c03757a3e4b76ca799b866b047d69a7b5
@@ -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);

// 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));

// 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
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
};

// 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);

// 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));

// 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)
}
}

// 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 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 5: Floats.
for display_item in display_list.floats.iter() {
display_item.draw_into_context(render_context, current_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)
}

// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
// Step 5: Floats.
for display_item in display_list.floats.iter() {
display_item.draw_into_context(&mut render_subcontext,
current_transform,
current_clip_stack)
}

// Step 7: Content.
for display_item in display_list.content.iter() {
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.

// Steps 8 and 9: Positioned descendants with nonnegative z-indices.
for positioned_kid in positioned_children.iter() {
if positioned_kid.z_index < 0 {
continue
// Step 7: Content.
for display_item in display_list.content.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);
// 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.
@@ -4,6 +4,7 @@

//! Painting of display lists using Moz2D/Azure.

use azure::azure::AzIntSize;
use azure::azure_hl::{B8G8R8A8, A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, ExtendClamp, GradientStop, Linear};
use azure::azure_hl::{LinearGradientPattern, LinearGradientPatternRef, SourceOp, StrokeOptions};
@@ -33,7 +34,7 @@ use text::TextRun;
use text::glyph::CharIndex;

pub struct RenderContext<'a> {
pub draw_target: &'a DrawTarget,
pub draw_target: DrawTarget,
pub font_ctx: &'a mut Box<FontContext>,
/// The rectangle that this context encompasses in page coordinates.
pub page_rect: Rect<f32>,
@@ -54,8 +55,8 @@ enum DashSize {
}

impl<'a> RenderContext<'a> {
pub fn get_draw_target(&self) -> &'a DrawTarget {
self.draw_target
pub fn get_draw_target(&self) -> &DrawTarget {
&self.draw_target
}

pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
@@ -153,7 +154,13 @@ impl<'a> RenderContext<'a> {
self.draw_target.fill_rect(&rect, ColorPatternRef(&pattern), Some(&draw_options));
}

fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, radius: &BorderRadii<AzFloat>, color: SideOffsets2D<Color>, style: SideOffsets2D<border_style::T>) {
fn draw_border_segment(&self,
direction: Direction,
bounds: &Rect<Au>,
border: SideOffsets2D<f32>,
radius: &BorderRadii<AzFloat>,
color: SideOffsets2D<Color>,
style: SideOffsets2D<border_style::T>) {
let (style_select, color_select) = match direction {
Top => (style.top, color.top),
Left => (style.left, color.left),
@@ -641,6 +648,49 @@ impl<'a> RenderContext<'a> {
LinearGradientPatternRef(&pattern),
None);
}

pub fn get_or_create_temporary_draw_target(&mut self, opacity: AzFloat) -> DrawTarget {
if opacity == 1.0 {
return self.draw_target.clone()
}

// FIXME(pcwalton): This surface might be bigger than necessary and waste memory.
let size = self.draw_target.get_size();
let size = Size2D {
width: size.width,
height: size.height,
};

let temporary_draw_target =
self.draw_target.create_similar_draw_target(&size, self.draw_target.get_format());
temporary_draw_target.set_transform(&self.draw_target.get_transform());
temporary_draw_target
}

/// If we created a temporary draw target, then draw it to the main draw target. This is called
/// after doing all the painting, and the temporary draw target must not be used afterward.
pub fn draw_temporary_draw_target_if_necessary(&mut self,
temporary_draw_target: &DrawTarget,
opacity: AzFloat) {
if (*temporary_draw_target) == self.draw_target {
// We're directly rendering to the surface; nothing to do.
return
}

let old_transform = self.draw_target.get_transform();
self.draw_target.set_transform(&Matrix2D::identity());
temporary_draw_target.set_transform(&Matrix2D::identity());
let rect = Rect(Point2D(0.0, 0.0), self.draw_target.get_size().to_azure_size());
let source_surface = temporary_draw_target.snapshot();
let draw_surface_options = DrawSurfaceOptions::new(Linear, true);
let draw_options = DrawOptions::new(opacity, 0);
self.draw_target.draw_surface(source_surface,
rect,
rect,
draw_surface_options,
draw_options);
self.draw_target.set_transform(&old_transform);
}
}

pub trait ToAzurePoint {
@@ -665,6 +715,16 @@ impl ToAzureRect for Rect<Au> {
}
}

pub trait ToAzureSize {
fn to_azure_size(&self) -> Size2D<AzFloat>;
}

impl ToAzureSize for AzIntSize {
fn to_azure_size(&self) -> Size2D<AzFloat> {
Size2D(self.width as AzFloat, self.height as AzFloat)
}
}

trait ToSideOffsetsPx {
fn to_float_px(&self) -> SideOffsets2D<AzFloat>;
}
@@ -505,7 +505,7 @@ impl WorkerThread {
{
// Build the render context.
let mut render_context = RenderContext {
draw_target: &draw_target,
draw_target: draw_target.clone(),
font_ctx: &mut self.font_context,
page_rect: tile.page_rect,
screen_rect: tile.screen_rect,
@@ -1722,20 +1722,31 @@ impl Flow for BlockFlow {
}

// Compute absolute position info for children.
let stacking_relative_position_of_absolute_containing_block_for_children =
if self.fragment.establishes_stacking_context() {
let logical_border_width = self.fragment.style().logical_border_width();
let position = LogicalPoint::new(self.base.writing_mode,
logical_border_width.inline_start,
logical_border_width.block_start);
let position = position.to_physical(self.base.writing_mode, container_size);
if self.is_positioned() {
position
} else {
// We establish a stacking context but are not positioned. (This will happen
// if, for example, the element has `position: static` but has `opacity` or
// `transform` set.) In this case, absolutely-positioned children will not be
// positioned relative to us but will instead be positioned relative to our
// containing block.
position - self.base.stacking_relative_position
}
} else {
self.base
.absolute_position_info
.stacking_relative_position_of_absolute_containing_block
};
let absolute_position_info_for_children = AbsolutePositionInfo {
stacking_relative_position_of_absolute_containing_block:
if self.fragment.establishes_stacking_context() {
let logical_border_width = self.fragment.style().logical_border_width();
LogicalPoint::new(self.base.writing_mode,
logical_border_width.inline_start,
logical_border_width.block_start).to_physical(
self.base.writing_mode,
container_size)
} else {
self.base
.absolute_position_info
.stacking_relative_position_of_absolute_containing_block
},
stacking_relative_position_of_absolute_containing_block_for_children,
relative_containing_block_size: self.fragment.content_box().size,
layers_needed_for_positioned_flows: self.base
.flags
@@ -1760,17 +1771,11 @@ impl Flow for BlockFlow {
origin_for_children +
(kid_base.position.start + relative_offset).to_physical(writing_mode,
container_size);
kid_base.absolute_position_info = absolute_position_info_for_children
}

flow::mut_base(kid).absolute_position_info = absolute_position_info_for_children;
flow::mut_base(kid).clip_rect = clip_rect
}

// Process absolute descendant links.
for absolute_descendant in self.base.abs_descendants.iter() {
flow::mut_base(absolute_descendant).absolute_position_info =
absolute_position_info_for_children
}
}

fn mark_as_root(&mut self) {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.