From 22a1c112d50e0c7f82937cc66afd8659b80d5574 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 31 Mar 2016 19:35:04 -0700 Subject: [PATCH] gfx: Simplify complex clipping regions as we construct them. This allows WebRender to correctly render complex clipping regions that can be reduced to single rounded rectangles. WebRender still can't render rounded rectangles with arbitrary intersections yet, but this allows it to handle many more cases. Closes servo/webrender#241. --- components/gfx/display_list/mod.rs | 38 ++++++++++++++++++- tests/wpt/mozilla/meta/MANIFEST.json | 24 ++++++++++++ .../css/border_radius_in_border_radius_a.html | 26 +++++++++++++ .../border_radius_in_border_radius_ref.html | 29 ++++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html create mode 100644 tests/wpt/mozilla/tests/css/border_radius_in_border_radius_ref.html diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 96eeebe84b1..2a132cf7f61 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -32,7 +32,7 @@ use range::Range; use serde::de::{self, Deserialize, Deserializer, MapVisitor, Visitor}; use serde::ser::impls::MapIteratorVisitor; use serde::ser::{Serialize, Serializer}; -use std::cmp::Ordering; +use std::cmp::{self, Ordering}; use std::collections::HashMap; use std::fmt; use std::hash::{BuildHasherDefault, Hash}; @@ -887,6 +887,27 @@ impl ClippingRegion { /// Intersects this clipping region with the given rounded rectangle. #[inline] pub fn intersect_with_rounded_rect(&mut self, rect: &Rect, radii: &BorderRadii) { + let new_complex_region = ComplexClippingRegion { + rect: *rect, + radii: *radii, + }; + + // FIXME(pcwalton): This is O(n²) worst case for disjoint clipping regions. Is that OK? + // They're slow anyway… + // + // Possibly relevant if we want to do better: + // + // http://www.inrg.csie.ntu.edu.tw/algorithm2014/presentation/D&C%20Lee-84.pdf + for existing_complex_region in &mut self.complex { + if existing_complex_region.completely_encloses(&new_complex_region) { + *existing_complex_region = new_complex_region; + return + } + if new_complex_region.completely_encloses(existing_complex_region) { + return + } + } + self.complex.push(ComplexClippingRegion { rect: *rect, radii: *radii, @@ -908,6 +929,21 @@ impl ClippingRegion { } } +impl ComplexClippingRegion { + // TODO(pcwalton): This could be more aggressive by considering points that touch the inside of + // the border radius ellipse. + fn completely_encloses(&self, other: &ComplexClippingRegion) -> bool { + let left = cmp::max(self.radii.top_left.width, self.radii.bottom_left.width); + let top = cmp::max(self.radii.top_left.height, self.radii.top_right.height); + let right = cmp::max(self.radii.top_right.width, self.radii.bottom_right.width); + let bottom = cmp::max(self.radii.bottom_left.height, self.radii.bottom_right.height); + let interior = Rect::new(Point2D::new(self.rect.origin.x + left, self.rect.origin.y + top), + Size2D::new(self.rect.size.width - left - right, + self.rect.size.height - top - bottom)); + interior.origin.x <= other.rect.origin.x && interior.origin.y <= other.rect.origin.y && + interior.max_x() >= other.rect.max_x() && interior.max_y() >= other.rect.max_y() + } +} /// Metadata attached to each display item. This is useful for performing auxiliary threads with /// the display list involving hit testing: finding the originating DOM node and determining the diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index fd4ce6b53a0..2a74039cf15 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -836,6 +836,18 @@ "url": "/_mozilla/css/border_radius_elliptical_a.html" } ], + "css/border_radius_in_border_radius_a.html": [ + { + "path": "css/border_radius_in_border_radius_a.html", + "references": [ + [ + "/_mozilla/css/border_radius_in_border_radius_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/border_radius_in_border_radius_a.html" + } + ], "css/border_radius_overlapping_a.html": [ { "path": "css/border_radius_overlapping_a.html", @@ -7246,6 +7258,18 @@ "url": "/_mozilla/css/border_radius_elliptical_a.html" } ], + "css/border_radius_in_border_radius_a.html": [ + { + "path": "css/border_radius_in_border_radius_a.html", + "references": [ + [ + "/_mozilla/css/border_radius_in_border_radius_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/border_radius_in_border_radius_a.html" + } + ], "css/border_radius_overlapping_a.html": [ { "path": "css/border_radius_overlapping_a.html", diff --git a/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html b/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html new file mode 100644 index 00000000000..f19170d0144 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html @@ -0,0 +1,26 @@ + + + + +
+ diff --git a/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_ref.html b/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_ref.html new file mode 100644 index 00000000000..0df74c58ddc --- /dev/null +++ b/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_ref.html @@ -0,0 +1,29 @@ + + + +
+