servo: Merge #11490 - layout: Fix display list construction of linear gradients (from emilio:linear-gradient); r=SimonSapin
authorEmilio Cobos Álvarez <me@emiliocobos.me>
Mon, 30 May 2016 17:28:34 -0500
changeset 338963 71ef0ad2ee23dc1588fa76ea2274325ed93cc839
parent 338962 5eb34c027070a2773a053403a82d338e325c48e5
child 338964 17ce4b8fcc4c14b5dafc72a73bcdaeec76b7a64a
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersSimonSapin
servo: Merge #11490 - layout: Fix display list construction of linear gradients (from emilio:linear-gradient); r=SimonSapin 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. --> Source-Repo: https://github.com/servo/servo Source-Revision: 134fd1876718e9e547873c5d1acf140ae5d6aaac
servo/components/layout/display_list_builder.rs
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -587,54 +587,61 @@ impl FragmentDisplayListBuilding for Fra
                                                          display_list_section: DisplayListSection,
                                                          absolute_bounds: &Rect<Au>,
                                                          clip: &ClippingRegion,
                                                          gradient: &LinearGradient,
                                                          style: &ServoComputedValues) {
         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));
 
         // 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 => {
                     if stop_run.is_none() {
                         // Initialize a new stop run.
                         let start_offset = if i == 0 {
                             0.0
                         } else {