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
This commit is contained in:
Peter Reid 2015-01-17 23:10:05 -05:00
parent 4a3e4032fe
commit c9cbb5b071
4 changed files with 46 additions and 3 deletions

View file

@ -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<Au>, radii: &BorderRadii<Au>) -> BorderRadii<Au> {
// 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<Au>, border_style: &Border) -> BorderRadii<Au> {
// 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<Au>, 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 {

View file

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

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<div style='box-sizing:border-box; width:6em; height:2em; border-radius:.5em 2em .5em 2em; background-color:red;'></div>
</body>
</html>

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<div style='box-sizing:border-box; width:6em; height:2em; border-radius:.4em 1.6em .4em 1.6em; background-color:red;'></div>
</body>
</html>