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

layout: Add support for `inset` to `box-shadow`

  • Loading branch information
pcwalton committed Dec 8, 2014
commit 721627af62fb848739d4393b2295adb3025d0392
@@ -587,6 +587,9 @@ pub struct BoxShadowDisplayItem {

/// The spread radius of this shadow.
pub spread_radius: Au,

/// True if this shadow is inset; false if it's outset.
pub inset: bool,
}

pub enum DisplayItemIterator<'a> {
@@ -674,7 +677,8 @@ impl DisplayItem {
&box_shadow.offset,
box_shadow.color,
box_shadow.blur_radius,
box_shadow.spread_radius)
box_shadow.spread_radius,
box_shadow.inset)
}

PseudoDisplayItemClass(_) => {}
@@ -7,7 +7,7 @@
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};
use azure::azure_hl::{LinearGradientPattern, LinearGradientPatternRef, Path, SourceOp};
use azure::azure_hl::{StrokeOptions};
use azure::scaled_font::ScaledFont;
use azure::{AZ_CAP_BUTT, AzFloat, struct__AzDrawOptions, struct__AzGlyph};
@@ -750,37 +750,53 @@ 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.
///
/// TODO(pcwalton): Add support for `inset`.
pub fn draw_box_shadow(&self,
_: &Rect<Au>,
box_bounds: &Rect<Au>,
offset: &Point2D<Au>,
color: Color,
blur_radius: Au,
spread_radius: Au) {
let sigma = blur_radius.to_subpx() as f32;
spread_radius: Au,
inset: bool) {
let shadow_bounds = box_bounds.translate(offset).inflate(spread_radius, spread_radius);

let path;
if inset {
path = self.create_rectangular_border_path(&self.tile_rect(), &shadow_bounds);
self.draw_push_clip(box_bounds)
} else {
path = self.create_rectangular_path(&shadow_bounds);
self.push_clip_outside_rect(box_bounds)
}

let shadow_draw_target_box_bounds = box_bounds.translate(offset).inflate(spread_radius,
spread_radius);
let path_builder = self.draw_target.create_path_builder();
path_builder.move_to(shadow_draw_target_box_bounds.origin.to_azure_point());
path_builder.line_to(Point2D(shadow_draw_target_box_bounds.max_x(),
shadow_draw_target_box_bounds.origin.y).to_azure_point());
path_builder.line_to(Point2D(shadow_draw_target_box_bounds.max_x(),
shadow_draw_target_box_bounds.max_y()).to_azure_point());
path_builder.line_to(Point2D(shadow_draw_target_box_bounds.origin.x,
shadow_draw_target_box_bounds.max_y()).to_azure_point());

self.push_clip_outside_rect(box_bounds);
self.draw_target.fill_with_blur(&path_builder.finish(),
self.draw_target.fill_with_blur(&path,
ColorPatternRef(&ColorPattern::new(color)),
sigma,
blur_radius.to_subpx() as f32,
None);
self.draw_target.pop_clip();
}

fn push_clip_outside_rect(&self, bounds: &Rect<Au>) {
/// Creates and returns a path that represents a rectangle.
fn create_rectangular_path(&self, rect: &Rect<Au>) -> Path {
let path_builder = self.draw_target.create_path_builder();
path_builder.move_to(rect.origin.to_azure_point());
path_builder.line_to(Point2D(rect.max_x(), rect.origin.y).to_azure_point());
path_builder.line_to(Point2D(rect.max_x(), rect.max_y()).to_azure_point());
path_builder.line_to(Point2D(rect.origin.x, rect.max_y()).to_azure_point());
path_builder.finish()
}

/// Creates and returns a path that represents a rectangular border. Like this:
///
/// +--------------------------------+
/// |################################|
/// |#######+---------------------+##|
/// |#######| |##|
/// |#######+---------------------+##|
/// |################################|
/// +--------------------------------+
fn create_rectangular_border_path(&self, outer_rect: &Rect<Au>, inner_rect: &Rect<Au>)
-> Path {
// +-----------+
// |2 |1
// | |
@@ -793,21 +809,30 @@ impl<'a> PaintContext<'a> {
// +-----------+
// 3 4

let bounds = bounds.to_azure_rect();
let tile_rect = Rect(Point2D(Au(0), Au(0)), Size2D(MAX_AU, MAX_AU)).to_azure_rect();

let (outer_rect, inner_rect) = (outer_rect.to_azure_rect(), inner_rect.to_azure_rect());
let path_builder = self.draw_target.create_path_builder();
path_builder.move_to(Point2D(tile_rect.max_x(), tile_rect.origin.y)); // 1
path_builder.line_to(Point2D(tile_rect.origin.x, tile_rect.origin.y)); // 2
path_builder.line_to(Point2D(tile_rect.origin.x, tile_rect.max_y())); // 3
path_builder.line_to(Point2D(tile_rect.max_x(), tile_rect.max_y())); // 4
path_builder.line_to(Point2D(tile_rect.max_x(), bounds.origin.y)); // 5
path_builder.line_to(Point2D(bounds.max_x(), bounds.origin.y)); // 6
path_builder.line_to(Point2D(bounds.max_x(), bounds.max_y())); // 7
path_builder.line_to(Point2D(bounds.origin.x, bounds.max_y())); // 8
path_builder.line_to(bounds.origin); // 9
path_builder.line_to(Point2D(tile_rect.max_x(), tile_rect.origin.y)); // 10
self.draw_target.push_clip(&path_builder.finish());
path_builder.move_to(Point2D(outer_rect.max_x(), outer_rect.origin.y)); // 1
path_builder.line_to(Point2D(outer_rect.origin.x, outer_rect.origin.y)); // 2
path_builder.line_to(Point2D(outer_rect.origin.x, outer_rect.max_y())); // 3
path_builder.line_to(Point2D(outer_rect.max_x(), outer_rect.max_y())); // 4
path_builder.line_to(Point2D(outer_rect.max_x(), inner_rect.origin.y)); // 5
path_builder.line_to(Point2D(inner_rect.max_x(), inner_rect.origin.y)); // 6
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.finish()
}

fn push_clip_outside_rect(&self, bounds: &Rect<Au>) {
self.draw_target.push_clip(&self.create_rectangular_border_path(&self.tile_rect(), bounds))
}

/// Returns an overapproximation of the boundaries of the tile.
///
/// FIXME(pcwalton): This is a HUGE overapproximation.
fn tile_rect(&self) -> Rect<Au> {
Rect(Point2D(Au(0), Au(0)), Size2D(MAX_AU, MAX_AU))
}
}

@@ -441,6 +441,7 @@ impl FragmentDisplayListBuilding for Fragment {
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,
}));
}
}
@@ -1253,6 +1253,7 @@ pub mod longhands {
pub blur_radius: specified::Length,
pub spread_radius: specified::Length,
pub color: Option<specified::CSSColor>,
pub inset: bool,
}
pub mod computed_value {
@@ -1268,6 +1269,7 @@ pub mod longhands {
pub blur_radius: Au,
pub spread_radius: Au,
pub color: computed::CSSColor,
pub inset: bool,
}
}
@@ -1295,56 +1297,81 @@ pub mod longhands {
blur_radius: computed::compute_Au(value.blur_radius, context),
spread_radius: computed::compute_Au(value.spread_radius, context),
color: value.color.unwrap_or(cssparser::CurrentColor),
inset: value.inset,
}
}).collect()
}
fn parse_one_box_shadow(iter: ParserIter) -> Result<SpecifiedBoxShadow,()> {
let mut lengths = [specified::Au_(Au(0)), ..4];
for (i, length) in lengths.iter_mut().enumerate() {
let mut lengths_parsed = 0;
let mut color = None;
let mut inset = false;
loop {
match iter.next() {
Some(value) => {
match specified::Length::parse(value) {
Ok(specified_length) => *length = specified_length,
Err(()) => {
iter.push_back(value);
if i < 2 {
// The first two lengths must be specified.
return Err(())
// Parse `inset`, if available.
match get_ident_lower(value) {
Ok(ref value) if value.as_slice() == "inset" && !inset => {
inset = true;
continue
}
_ => iter.push_back(value),
}
// Parse lengths.
if lengths_parsed > 0 {
break
}
loop {
match iter.next() {
Some(value) => {
match specified::Length::parse(value) {
Ok(the_length) if lengths_parsed < 4 => {
lengths[lengths_parsed] = the_length;
lengths_parsed += 1
}
Ok(_) => return Err(()),
Err(()) => {
iter.push_back(value);
break
}
}
}
break
None => break,
}
}
}
None => {
if i < 2 {
// The first two lengths must be specified.
// The first two lengths must be specified.
if lengths_parsed < 2 {
return Err(())
}
break
}
}
}
let color = match iter.next() {
Some(value) => {
match specified::CSSColor::parse(value) {
Ok(color) => Some(color),
Err(()) => {
iter.push_back(value);
None
// Parse the color.
match iter.next() {
Some(value) => {
match specified::CSSColor::parse(value) {
Ok(the_color) => color = Some(the_color),
Err(()) => iter.push_back(value),
}
}
None => {}
}
}
None => break,
}
None => None,
};
}
Ok(SpecifiedBoxShadow {
offset_x: lengths[0],
offset_y: lengths[1],
blur_radius: lengths[2],
spread_radius: lengths[3],
color: color,
inset: inset,
})
}
</%self:longhand>
@@ -193,3 +193,5 @@ fragment=top != ../html/acid2.html acid2_ref.html
== box_shadow_default_color_a.html box_shadow_default_color_ref.html
== box_shadow_paint_order_a.html box_shadow_paint_order_ref.html
== box_shadow_spread_a.html box_shadow_spread_ref.html
== box_shadow_inset_a.html box_shadow_inset_ref.html
== box_shadow_inset_parsing_a.html box_shadow_inset_parsing_ref.html
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<head>
<!-- Tests that box-shadow inset works. -->
<style>
section {
position: absolute;
width: 100px;
height: 100px;
top: 100px;
left: 100px;
box-shadow: inset 50px 10px gold;
}
</style>
</head>
<body>
<section></section>
</body>

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<head>
<!-- Tests that box-shadow inset parsing quirks work properly. -->
<style>
section {
position: absolute;
width: 100px;
height: 100px;
top: 100px;
left: 100px;
border: solid black 1px;
box-shadow: inset 0 0 1em gold;
}
</style>
</head>
<body>
<section></section>
</body>


@@ -0,0 +1,21 @@
<!DOCTYPE html>
<head>
<!-- Tests that box-shadow inset parsing quirks work properly. -->
<style>
section {
position: absolute;
width: 100px;
height: 100px;
top: 100px;
left: 100px;
border: solid black 1px;
box-shadow: 0 0 1em gold inset;
}
</style>
</head>
<body>
<section></section>
</body>



@@ -0,0 +1,28 @@
<!DOCTYPE html>
<head>
<!-- Tests that box-shadow inset works. -->
<style>
section {
position: absolute;
width: 100px;
height: 100px;
top: 100px;
left: 100px;
background: gold;
}
nav {
display: block;
position: absolute;
width: 50px;
height: 90px;
background: white;
left: 50px;
top: 10px;
}
</style>
</head>
<body>
<section><nav></nav></section>
</body>


ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.