Auto merge of #11490 - emilio:linear-gradient, r=SimonSapin

layout: Fix display list construction of linear gradients

Fixes #11486

The previous code assumed that the diagonals of the elements were
perpendicular, which only happens with squares.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #11486 (github issue number if applicable).

<!-- Either: -->
- [x] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11490)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-05-30 17:28:34 -05:00
commit 134fd18767
4 changed files with 114 additions and 26 deletions

View file

@ -592,35 +592,41 @@ 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) => {
let angle = match gradient.angle_or_corner {
AngleOrCorner::Angle(angle) => angle.radians(),
AngleOrCorner::Corner(horizontal, vertical) => {
// 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.radians().sin(), -angle.radians().cos());
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();
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))
}
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 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(
@ -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 => {

View file

@ -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",

View file

@ -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>

View file

@ -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>