diff options
author | Emilio Cobos Álvarez <me@emiliocobos.me> | 2016-05-28 18:06:59 +0200 |
---|---|---|
committer | Emilio Cobos Álvarez <me@emiliocobos.me> | 2016-05-31 00:01:22 +0200 |
commit | 37d1c749aa1b78c6c8157af6bd2ef7944835cdc8 (patch) | |
tree | e76165b19a1184d8b7aeb0b8fedf7d695245ad44 | |
parent | 5668a55ef43f7eab701c802736e451d8bdacab83 (diff) | |
download | servo-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.rs | 59 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/MANIFEST.json | 24 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/css/linear_gradients_non_square_a.html | 29 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/css/linear_gradients_non_square_ref.html | 28 |
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> |