mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
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:
parent
873ca6cadd
commit
1c1c507c03
17 changed files with 421 additions and 146 deletions
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue