aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <me@emiliocobos.me>2016-05-28 18:06:59 +0200
committerEmilio Cobos Álvarez <me@emiliocobos.me>2016-05-31 00:01:22 +0200
commit37d1c749aa1b78c6c8157af6bd2ef7944835cdc8 (patch)
treee76165b19a1184d8b7aeb0b8fedf7d695245ad44
parent5668a55ef43f7eab701c802736e451d8bdacab83 (diff)
downloadservo-37d1c749aa1b78c6c8157af6bd2ef7944835cdc8.tar.gz
servo-37d1c749aa1b78c6c8157af6bd2ef7944835cdc8.zip
layout: Correctly calculate the angle for gradients with corners
The previous code assumed that the diagonals of the elements were perpendicular, which only happens with squares. tests: layout: Test linear gradient corners
-rw-r--r--components/layout/display_list_builder.rs59
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json24
-rw-r--r--tests/wpt/mozilla/tests/css/linear_gradients_non_square_a.html29
-rw-r--r--tests/wpt/mozilla/tests/css/linear_gradients_non_square_ref.html28
4 files changed, 114 insertions, 26 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index e9d7f02bad3..8e1d10424db 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -592,36 +592,42 @@ impl FragmentDisplayListBuilding for Fragment {
let mut clip = clip.clone();
clip.intersect_rect(absolute_bounds);
- // This is the distance between the center and the ending point; i.e. half of the distance
- // between the starting point and the ending point.
- let delta = match gradient.angle_or_corner {
- AngleOrCorner::Angle(angle) => {
- // Get correct gradient line length, based on:
- // https://drafts.csswg.org/css-images-3/#linear-gradients
- let dir = Point2D::new(angle.radians().sin(), -angle.radians().cos());
- let line_length = (dir.x * absolute_bounds.size.width.to_f32_px()).abs() +
- (dir.y * absolute_bounds.size.height.to_f32_px()).abs();
-
- let inv_dir_length = 1.0 / (dir.x * dir.x + dir.y * dir.y).sqrt();
-
- Point2D::new(Au::from_f32_px(dir.x * inv_dir_length * line_length / 2.0),
- Au::from_f32_px(dir.y * inv_dir_length * line_length / 2.0))
- }
+ let angle = match gradient.angle_or_corner {
+ AngleOrCorner::Angle(angle) => angle.radians(),
AngleOrCorner::Corner(horizontal, vertical) => {
- let x_factor = match horizontal {
- HorizontalDirection::Left => -1,
- HorizontalDirection::Right => 1,
- };
- let y_factor = match vertical {
- VerticalDirection::Top => -1,
- VerticalDirection::Bottom => 1,
- };
- Point2D::new(absolute_bounds.size.width * x_factor / 2,
- absolute_bounds.size.height * y_factor / 2)
+ // This the angle for one of the diagonals of the box. Our angle
+ // will either be this one, this one + PI, or one of the other
+ // two perpendicular angles.
+ let atan = (absolute_bounds.size.height.to_f32_px() /
+ absolute_bounds.size.width.to_f32_px()).atan();
+ match (horizontal, vertical) {
+ (HorizontalDirection::Right, VerticalDirection::Bottom)
+ => f32::consts::PI - atan,
+ (HorizontalDirection::Left, VerticalDirection::Bottom)
+ => f32::consts::PI + atan,
+ (HorizontalDirection::Right, VerticalDirection::Top)
+ => atan,
+ (HorizontalDirection::Left, VerticalDirection::Top)
+ => -atan,
+ }
}
};
+ // Get correct gradient line length, based on:
+ // https://drafts.csswg.org/css-images-3/#linear-gradients
+ let dir = Point2D::new(angle.sin(), -angle.cos());
+
+ let line_length = (dir.x * absolute_bounds.size.width.to_f32_px()).abs() +
+ (dir.y * absolute_bounds.size.height.to_f32_px()).abs();
+
+ let inv_dir_length = 1.0 / (dir.x * dir.x + dir.y * dir.y).sqrt();
+
+ // This is the vector between the center and the ending point; i.e. half
+ // of the distance between the starting point and the ending point.
+ let delta = Point2D::new(Au::from_f32_px(dir.x * inv_dir_length * line_length / 2.0),
+ Au::from_f32_px(dir.y * inv_dir_length * line_length / 2.0));
+
// This is the length of the gradient line.
let length = Au::from_f32_px(
(delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0));
@@ -629,7 +635,8 @@ impl FragmentDisplayListBuilding for Fragment {
// Determine the position of each stop per CSS-IMAGES § 3.4.
//
// FIXME(#3908, pcwalton): Make sure later stops can't be behind earlier stops.
- let (mut stops, mut stop_run) = (Vec::new(), None);
+ let mut stops = Vec::with_capacity(gradient.stops.len());
+ let mut stop_run = None;
for (i, stop) in gradient.stops.iter().enumerate() {
let offset = match stop.position {
None => {
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index 784c0ba7451..05e8dc4ad7e 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -3120,6 +3120,18 @@
"url": "/_mozilla/css/linear_gradients_lengths_a.html"
}
],
+ "css/linear_gradients_non_square_a.html": [
+ {
+ "path": "css/linear_gradients_non_square_a.html",
+ "references": [
+ [
+ "/_mozilla/css/linear_gradients_non_square_ref.html",
+ "=="
+ ]
+ ],
+ "url": "/_mozilla/css/linear_gradients_non_square_a.html"
+ }
+ ],
"css/linear_gradients_parsing_a.html": [
{
"path": "css/linear_gradients_parsing_a.html",
@@ -10124,6 +10136,18 @@
"url": "/_mozilla/css/linear_gradients_lengths_a.html"
}
],
+ "css/linear_gradients_non_square_a.html": [
+ {
+ "path": "css/linear_gradients_non_square_a.html",
+ "references": [
+ [
+ "/_mozilla/css/linear_gradients_non_square_ref.html",
+ "=="
+ ]
+ ],
+ "url": "/_mozilla/css/linear_gradients_non_square_a.html"
+ }
+ ],
"css/linear_gradients_parsing_a.html": [
{
"path": "css/linear_gradients_parsing_a.html",
diff --git a/tests/wpt/mozilla/tests/css/linear_gradients_non_square_a.html b/tests/wpt/mozilla/tests/css/linear_gradients_non_square_a.html
new file mode 100644
index 00000000000..2edd6cbaa46
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/linear_gradients_non_square_a.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Linear gradients for non-square elements</title>
+<link rel="match" href="linear_gradients_non_square_ref.html">
+<style>
+.a,
+.b,
+.c,
+.d {
+ width: 200px;
+ height: 100px;
+}
+.a {
+ background: linear-gradient(to right bottom, black 50%, lightgray 50%);
+}
+.b {
+ background: linear-gradient(to left bottom, black 50%, lightgray 50%);
+}
+.c {
+ background: linear-gradient(to left top, black 50%, lightgray 50%);
+}
+.d {
+ background: linear-gradient(to right top, black 50%, lightgray 50%);
+}
+</style>
+<div class="a"></div>
+<div class="b"></div>
+<div class="c"></div>
+<div class="d"></div>
diff --git a/tests/wpt/mozilla/tests/css/linear_gradients_non_square_ref.html b/tests/wpt/mozilla/tests/css/linear_gradients_non_square_ref.html
new file mode 100644
index 00000000000..1ad3c75747e
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/linear_gradients_non_square_ref.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Linear gradients for non-square elements reference</title>
+<style>
+.a,
+.b,
+.c,
+.d {
+ width: 200px;
+ height: 100px;
+}
+.a {
+ background: linear-gradient(-206.56505118deg, black 50%, lightgray 50%);
+}
+.b {
+ background: linear-gradient(206.56505118deg, black 50%, lightgray 50%);
+}
+.c {
+ background: linear-gradient(-26.56505118deg, black 50%, lightgray 50%);
+}
+.d {
+ background: linear-gradient(26.56505118deg, black 50%, lightgray 50%);
+}
+</style>
+<div class="a"></div>
+<div class="b"></div>
+<div class="c"></div>
+<div class="d"></div>