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

gfx: Implement most of `box-shadow` per CSS-BACKGROUNDS. #3940

Closed
wants to merge 6 commits into from
Prev

Fix some clipping and address some merge fallout

  • Loading branch information
pcwalton committed Dec 9, 2014
commit 803d61c1a8d9b1eace74b42fd79ca8f8a1708efb
@@ -29,7 +29,7 @@ use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::LayerId;
use servo_net::image::base::Image;
use servo_util::dlist as servo_dlist;
use servo_util::geometry::{mod, Au};
use servo_util::geometry::{mod, Au, ZERO_POINT};
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec8};
use std::fmt;
@@ -171,7 +171,7 @@ impl StackingContext {
display_list: display_list,
layer: layer,
bounds: bounds,
clip_rect: bounds,
clip_rect: Rect(ZERO_POINT, bounds.size),
z_index: z_index,
opacity: opacity,
}
@@ -182,7 +182,7 @@ impl StackingContext {
paint_context: &mut PaintContext,
tile_bounds: &Rect<AzFloat>,
transform: &Matrix2D<AzFloat>,
clip_rect: Option<&Rect<Au>>) {
clip_rect: Option<Rect<Au>>) {
let temporary_draw_target =
paint_context.get_or_create_temporary_draw_target(self.opacity);
{
@@ -191,6 +191,7 @@ impl StackingContext {
font_ctx: &mut *paint_context.font_ctx,
page_rect: paint_context.page_rect,
screen_rect: paint_context.screen_rect,
clip_rect: clip_rect,
transient_clip_rect: None,
};

@@ -207,12 +208,9 @@ impl StackingContext {
.sort_by(|this, other| this.z_index.cmp(&other.z_index));

// Set up our clip rect and transform.
match clip_rect {
None => {}
Some(clip_rect) => paint_subcontext.draw_push_clip(clip_rect),
}
let old_transform = paint_subcontext.draw_target.get_transform();
paint_subcontext.draw_target.set_transform(transform);
paint_subcontext.push_clip_if_applicable();

// Steps 1 and 2: Borders and background for the root.
for display_item in display_list.background_and_borders.iter() {
@@ -240,7 +238,7 @@ impl StackingContext {
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
&new_tile_rect,
&new_transform,
Some(&positioned_kid.clip_rect))
Some(positioned_kid.clip_rect))
}
}

@@ -283,21 +281,16 @@ impl StackingContext {
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
&new_tile_rect,
&new_transform,
Some(&positioned_kid.clip_rect))
Some(positioned_kid.clip_rect))
}
}

// TODO(pcwalton): Step 10: Outlines.

// Undo our clipping and transform.
if paint_subcontext.transient_clip_rect.is_some() {
paint_subcontext.draw_pop_clip();
paint_subcontext.transient_clip_rect = None
}
paint_subcontext.draw_target.set_transform(&old_transform);
if clip_rect.is_some() {
paint_subcontext.draw_pop_clip()
}
paint_subcontext.remove_transient_clip_if_applicable();
paint_subcontext.pop_clip_if_applicable();
paint_subcontext.draw_target.set_transform(&old_transform)
}

paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target, self.opacity)
@@ -672,13 +665,12 @@ impl DisplayItem {
}

BoxShadowDisplayItemClass(ref box_shadow) => {
render_context.draw_box_shadow(&box_shadow.base.bounds,
&box_shadow.box_bounds,
&box_shadow.offset,
box_shadow.color,
box_shadow.blur_radius,
box_shadow.spread_radius,
box_shadow.inset)
paint_context.draw_box_shadow(&box_shadow.box_bounds,
&box_shadow.offset,
box_shadow.color,
box_shadow.blur_radius,
box_shadow.spread_radius,
box_shadow.inset)
}

PseudoDisplayItemClass(_) => {}
@@ -58,8 +58,7 @@ impl DisplayListOptimizer {
mut stacking_contexts: I)
where I: Iterator<&'a Arc<StackingContext>> {
for stacking_context in stacking_contexts {
if self.visible_rect.intersects(&stacking_context.bounds) &&
self.visible_rect.intersects(&stacking_context.clip_rect) {
if self.visible_rect.intersects(&stacking_context.bounds) {
result_list.push_back((*stacking_context).clone())
}
}
@@ -5,14 +5,16 @@
//! 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, Path, SourceOp};
use azure::azure_hl::{StrokeOptions, OverOp};
use azure::azure_hl::{A8, B8G8R8A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, ExtendClamp, GaussianBlurFilterType};
use azure::azure_hl::{GaussianBlurInput, GradientStop, Linear, LinearGradientPattern};
use azure::azure_hl::{LinearGradientPatternRef, Path, SourceOp, StdDeviationGaussianBlurAttribute};
use azure::azure_hl::{StrokeOptions};
use azure::scaled_font::ScaledFont;
use azure::{AZ_CAP_BUTT, AzFloat, struct__AzDrawOptions, struct__AzGlyph};
use azure::{struct__AzGlyphBuffer, struct__AzPoint, AzDrawTargetFillGlyphs};
use display_list::{SidewaysLeft, SidewaysRight, TextDisplayItem, Upright};
use display_list::{BOX_SHADOW_INFLATION_FACTOR, BorderRadii, SidewaysLeft, SidewaysRight};
use display_list::{TextDisplayItem, Upright};
use font_context::FontContext;
use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
@@ -23,7 +25,7 @@ use libc::size_t;
use libc::types::common::c99::{uint16_t, uint32_t};
use png::{RGB8, RGBA8, K8, KA8};
use servo_net::image::base::Image;
use servo_util::geometry::{Au, MAX_AU, ZERO_POINT};
use servo_util::geometry::{Au, MAX_RECT};
use servo_util::opts;
use servo_util::range::Range;
use std::default::Default;
@@ -41,16 +43,12 @@ pub struct PaintContext<'a> {
pub page_rect: Rect<f32>,
/// The rectangle that this context encompasses in screen coordinates (pixels).
pub screen_rect: Rect<uint>,
/// The clipping rect for the stacking context as a whole.
pub clip_rect: Option<Rect<Au>>,
/// The current transient clipping rect, if any. A "transient clipping rect" is the clipping
/// rect used by the last display item. We cache the last value so that we avoid pushing and
/// popping clip rects unnecessarily.
pub transient_clip_rect: Option<Rect<Au>>,
/// The factor by which this tile is zoomed in. Typically pinch zooming is the reason why this
/// will have a value other than 1.0.
///
/// Unlike much of the rest of the graphics code, typed units aren't useful for this value
/// because it's only used in conjunction with the Azure API, which doesn't use typed units.
pub scale: AzFloat,
}

enum Direction {
@@ -65,7 +63,7 @@ enum DashSize {
DashedBorder = 3
}

impl<'a> PaintContext<'a> {
impl<'a> PaintContext<'a> {
pub fn get_draw_target(&self) -> &DrawTarget {
&self.draw_target
}
@@ -154,7 +152,7 @@ impl<'a> PaintContext<'a> {
}

pub fn clear(&self) {
let pattern = ColorPattern::new(Color::new(1.0, 1.0, 1.0, 0.0));
let pattern = ColorPattern::new(Color::new(0.0, 0.0, 0.0, 0.0));
let rect = Rect(Point2D(self.page_rect.origin.x as AzFloat,
self.page_rect.origin.y as AzFloat),
Size2D(self.screen_rect.size.width as AzFloat,
@@ -748,35 +746,94 @@ impl<'a> PaintContext<'a> {
}

/// Draws a box shadow with the given boundaries, color, offset, blur radius, and spread
/// radius. `bounds` is just a rectangle that must be large enough to encompass all the ink in
/// this box shadow. `box_bounds` represents the boundaries of the box itself.
pub fn draw_box_shadow(&self,
_: &Rect<Au>,
/// radius. `box_bounds` represents the boundaries of the box.
pub fn draw_box_shadow(&mut self,
box_bounds: &Rect<Au>,
offset: &Point2D<Au>,
color: Color,
blur_radius: Au,
spread_radius: Au,
inset: bool) {
let shadow_bounds = box_bounds.translate(offset).inflate(spread_radius, spread_radius);
// Remove both the transient clip and the stacking context clip, because we may need to
// draw outside the stacking context's clip.
self.remove_transient_clip_if_applicable();
self.pop_clip_if_applicable();

// Create a new draw target that's the same size as this tile, but with enough space around
// the edges to hold the entire blur. (If we don't do this, then there will be seams
// between tiles.)
//
// FIXME(pcwalton): This draw target might be larger than necessary and waste memory.
let draw_target_size = self.draw_target.get_size();
let draw_target_size = Size2D(draw_target_size.width, draw_target_size.height);
let side_inflation = (blur_radius * BOX_SHADOW_INFLATION_FACTOR).to_subpx().ceil() as i32;
let inflated_draw_target_size = Size2D(draw_target_size.width + side_inflation * 2,
draw_target_size.height + side_inflation * 2);
let temporary_draw_target =
self.draw_target.create_similar_draw_target(&inflated_draw_target_size,
self.draw_target.get_format());
let draw_target_transform = self.draw_target.get_transform();
temporary_draw_target.set_transform(
&Matrix2D::identity().translate(side_inflation as AzFloat, side_inflation as AzFloat)
.mul(&draw_target_transform));

let shadow_bounds = box_bounds.translate(offset).inflate(spread_radius, spread_radius);
let path;
let big_rect = Rect(Point2D(Au(0), Au(0)), Size2D(MAX_AU, MAX_AU));
if inset {
path = self.draw_target.create_rectangular_border_path(&big_rect, &shadow_bounds);
path = temporary_draw_target.create_rectangular_border_path(&MAX_RECT, &shadow_bounds);
self.draw_target.push_clip(&self.draw_target.create_rectangular_path(box_bounds))
} else {
path = self.draw_target.create_rectangular_path(&shadow_bounds);
path = temporary_draw_target.create_rectangular_path(&shadow_bounds);
self.draw_target.push_clip(&self.draw_target
.create_rectangular_border_path(&big_rect, box_bounds))
.create_rectangular_border_path(&MAX_RECT, box_bounds))
}

self.draw_target.draw_shadow(&path,
&color,
&ZERO_POINT.to_azure_point(),
blur_radius.to_subpx() as AzFloat,
OverOp);
temporary_draw_target.fill(&path, &ColorPattern::new(color), &DrawOptions::new(1.0, 0));

// Go ahead and create the blur now. Despite the name, Azure's notion of `StdDeviation`
// describes the blur radius, not the sigma for the Gaussian blur.
let blur_filter = self.draw_target.create_filter(GaussianBlurFilterType);
blur_filter.set_attribute(StdDeviationGaussianBlurAttribute(blur_radius.to_subpx() as
AzFloat));
blur_filter.set_input(GaussianBlurInput, &temporary_draw_target.snapshot());

// Blit the blur onto the tile. We undo the transforms here because we want to directly
// stack the temporary draw target onto the tile.
temporary_draw_target.set_transform(&Matrix2D::identity());
self.draw_target.set_transform(&Matrix2D::identity());
self.draw_target.draw_filter(blur_filter,
&Rect(Point2D(0.0, 0.0),
Size2D(inflated_draw_target_size.width as AzFloat,
inflated_draw_target_size.height as AzFloat)),
&Point2D(-side_inflation as AzFloat,
-side_inflation as AzFloat),
DrawOptions::new(1.0, 0));

self.draw_target.set_transform(&draw_target_transform);
self.draw_target.pop_clip();

// Push back the stacking context clip.
self.push_clip_if_applicable();
}

pub fn push_clip_if_applicable(&self) {
match self.clip_rect {
None => {}
Some(ref clip_rect) => self.draw_push_clip(clip_rect),
}
}

pub fn pop_clip_if_applicable(&self) {
if self.clip_rect.is_some() {
self.draw_pop_clip()
}
}

pub fn remove_transient_clip_if_applicable(&mut self) {
if self.transient_clip_rect.is_some() {
self.draw_pop_clip();
self.transient_clip_rect = None
}
}
}

@@ -985,7 +1042,7 @@ impl DrawTargetExtensions for DrawTarget {
path_builder.line_to(Point2D(inner_rect.max_x(), inner_rect.max_y())); // 7
path_builder.line_to(Point2D(inner_rect.origin.x, inner_rect.max_y())); // 8
path_builder.line_to(inner_rect.origin); // 9
path_builder.line_to(Point2D(outer_rect.max_x(), outer_rect.origin.y)); // 10
path_builder.line_to(Point2D(outer_rect.max_x(), inner_rect.origin.y)); // 10
path_builder.finish()
}

@@ -509,8 +509,8 @@ impl WorkerThread {
font_ctx: &mut self.font_context,
page_rect: tile.page_rect,
screen_rect: tile.screen_rect,
clip_rect: None,
transient_clip_rect: None,
scale: scale,
};

// Apply the translation to paint the tile we want.
@@ -24,15 +24,15 @@ use util::{OpaqueNodeMethods, ToGfxColor};
use geom::approxeq::ApproxEq;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use gfx::color;
use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, BackgroundAndBorderLevel, BaseDisplayItem};
use gfx::display_list::{BorderDisplayItem, BorderDisplayItemClass, BoxShadowDisplayItem};
use gfx::display_list::{BoxShadowDisplayItemClass, ContentStackingLevel, DisplayList};
use gfx::display_list::{FloatStackingLevel, GradientDisplayItem, GradientDisplayItemClass};
use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BorderDisplayItemClass, BorderRadii, BoxShadowDisplayItem};
use gfx::display_list::{BoxShadowDisplayItemClass, DisplayItem, DisplayList};
use gfx::display_list::{GradientDisplayItem, GradientDisplayItemClass};
use gfx::display_list::{GradientStop, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
use gfx::display_list::{LineDisplayItemClass, PositionedDescendantStackingLevel};
use gfx::display_list::{PseudoDisplayItemClass, RootOfStackingContextLevel, SidewaysLeft};
use gfx::display_list::{LineDisplayItemClass};
use gfx::display_list::{PseudoDisplayItemClass, SidewaysLeft};
use gfx::display_list::{SidewaysRight, SolidColorDisplayItem, SolidColorDisplayItemClass};
use gfx::display_list::{StackingLevel, TextDisplayItem, TextDisplayItemClass, Upright};
use gfx::display_list::{StackingContext, TextDisplayItem, TextDisplayItemClass, Upright};
use gfx::paint_task::PaintLayer;
use servo_msg::compositor_msg::{FixedPosition, Scrollable};
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg};
@@ -435,14 +435,14 @@ impl FragmentDisplayListBuilding for Fragment {
absolute_bounds.translate(&Point2D(box_shadow.offset_x, box_shadow.offset_y))
.inflate(inflation, inflation);
list.push(BoxShadowDisplayItemClass(box BoxShadowDisplayItem {
base: BaseDisplayItem::new(bounds, self.node, level, *clip_rect),
base: BaseDisplayItem::new(bounds, self.node, *clip_rect),
box_bounds: *absolute_bounds,
color: style.resolve_color(box_shadow.color).to_gfx_color(),
offset: Point2D(box_shadow.offset_x, box_shadow.offset_y),
blur_radius: box_shadow.blur_radius,
spread_radius: box_shadow.spread_radius,
inset: box_shadow.inset,
}));
}), level);
}
}

@@ -8,7 +8,7 @@
top: 100px;
left: 100px;
border: solid black 1px;
box-shadow: 10px 10px 5px;
box-shadow: 10px 10px 5px 0px;
}
</style>
</head>
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.