From c9cbb5b071f6a3a76d3f25e74ece52ce580c26c9 Mon Sep 17 00:00:00 2001 From: Peter Reid Date: Sat, 17 Jan 2015 23:10:05 -0500 Subject: [PATCH] Handle overlapping border-radius corners Conforming to section 5.5 (Rounded Corners/Overlapping Curves) of "CSS Background and Borders Module Level 3", border radii on elements whose border curves would have overlapped are uniformly scaled down to the point that they no longer do. http://dev.w3.org/csswg/css-backgrounds/#corner-overlap --- components/layout/display_list_builder.rs | 36 ++++++++++++++++++-- tests/ref/basic.list | 1 + tests/ref/border_radius_overlapping_a.html | 6 ++++ tests/ref/border_radius_overlapping_ref.html | 6 ++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 tests/ref/border_radius_overlapping_a.html create mode 100644 tests/ref/border_radius_overlapping_ref.html diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 44d9a286bf2..1c81e65459b 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -40,7 +40,7 @@ use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_msg::constellation_msg::ConstellationChan; use servo_net::image::holder::ImageHolder; use servo_util::cursor::Cursor; -use servo_util::geometry::{self, Au, to_px}; +use servo_util::geometry::{self, Au, to_px, to_frac_px}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; use std::default::Default; @@ -218,12 +218,42 @@ pub trait FragmentDisplayListBuilding { clip: &ClippingRegion); } +fn handle_overlapping_radii(size: &Size2D, radii: &BorderRadii) -> BorderRadii { + // No two corners' border radii may add up to more than the length of the edge + // between them. To prevent that, all radii are scaled down uniformly. + fn scale_factor(radius_a: Au, radius_b: Au, edge_length: Au) -> f64 { + let required = radius_a + radius_b; + + if required <= edge_length { + 1.0 + } else { + to_frac_px(edge_length) / to_frac_px(required) + } + } + + let top_factor = scale_factor(radii.top_left, radii.top_right, size.width); + let bottom_factor = scale_factor(radii.bottom_left, radii.bottom_right, size.width); + let left_factor = scale_factor(radii.top_left, radii.bottom_left, size.height); + let right_factor = scale_factor(radii.top_right, radii.bottom_right, size.height); + let min_factor = top_factor.min(bottom_factor).min(left_factor).min(right_factor); + if min_factor < 1.0 { + BorderRadii { + top_left: radii.top_left .scale_by(min_factor), + top_right: radii.top_right .scale_by(min_factor), + bottom_left: radii.bottom_left .scale_by(min_factor), + bottom_right: radii.bottom_right.scale_by(min_factor), + } + } else { + *radii + } +} + fn build_border_radius(abs_bounds: &Rect, border_style: &Border) -> BorderRadii { // TODO(cgaebel): Support border radii even in the case of multiple border widths. // This is an extension of supporting elliptical radii. For now, all percentage // radii will be relative to the width. - BorderRadii { + handle_overlapping_radii(&abs_bounds.size, &BorderRadii { top_left: model::specified(border_style.border_top_left_radius, abs_bounds.size.width), top_right: model::specified(border_style.border_top_right_radius, @@ -232,7 +262,7 @@ fn build_border_radius(abs_bounds: &Rect, border_style: &Border) -> BorderRa abs_bounds.size.width), bottom_left: model::specified(border_style.border_bottom_left_radius, abs_bounds.size.width), - } + }) } impl FragmentDisplayListBuilding for Fragment { diff --git a/tests/ref/basic.list b/tests/ref/basic.list index e53bcc6da93..3587b59e181 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -223,6 +223,7 @@ fragment=top != ../html/acid2.html acid2_ref.html != inset_blackborder.html blackborder_ref.html != outset_blackborder.html blackborder_ref.html == border_radius_clip_a.html border_radius_clip_ref.html +== border_radius_overlapping_a.html border_radius_overlapping_ref.html == stacking_context_overflow_a.html stacking_context_overflow_ref.html == stacking_context_overflow_relative_outline_a.html stacking_context_overflow_relative_outline_ref.html == word_break_a.html word_break_ref.html diff --git a/tests/ref/border_radius_overlapping_a.html b/tests/ref/border_radius_overlapping_a.html new file mode 100644 index 00000000000..58baa1720ca --- /dev/null +++ b/tests/ref/border_radius_overlapping_a.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/tests/ref/border_radius_overlapping_ref.html b/tests/ref/border_radius_overlapping_ref.html new file mode 100644 index 00000000000..287a3d52085 --- /dev/null +++ b/tests/ref/border_radius_overlapping_ref.html @@ -0,0 +1,6 @@ + + + +
+ +