Auto merge of #7502 - bjwbell:elliptical-borders, r=pcwalton

gfx: Add elliptical border radius support

TODO: Add code for parsing shorthand border-radius e.g. "border-radius: 10px 5% / 20px".

r? @pcwalton

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/7502)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2015-09-04 18:46:24 -06:00
commit 5bad6b1b6e
9 changed files with 603 additions and 305 deletions

View file

@ -975,12 +975,44 @@ pub struct BorderDisplayItem {
/// Information about the border radii. /// Information about the border radii.
/// ///
/// TODO(pcwalton): Elliptical radii. /// TODO(pcwalton): Elliptical radii.
#[derive(Clone, Default, PartialEq, Debug, Copy, HeapSizeOf, Deserialize, Serialize)] #[derive(Clone, PartialEq, Debug, Copy, HeapSizeOf, Deserialize, Serialize)]
pub struct BorderRadii<T> { pub struct BorderRadii<T> {
pub top_left: T, pub top_left: Size2D<T>,
pub top_right: T, pub top_right: Size2D<T>,
pub bottom_right: T, pub bottom_right: Size2D<T>,
pub bottom_left: T, pub bottom_left: Size2D<T>,
}
impl<T> Default for BorderRadii<T> where T: Default, T: Clone {
fn default() -> Self {
let top_left = Size2D::new(Default::default(),
Default::default());
let top_right = Size2D::new(Default::default(),
Default::default());
let bottom_left = Size2D::new(Default::default(),
Default::default());
let bottom_right = Size2D::new(Default::default(),
Default::default());
BorderRadii { top_left: top_left,
top_right: top_right,
bottom_left: bottom_left,
bottom_right: bottom_right }
}
}
impl BorderRadii<Au> {
// Scale the border radii by the specified factor
pub fn scale_by(&self, s: f32) -> BorderRadii<Au> {
BorderRadii { top_left: BorderRadii::scale_corner_by(self.top_left, s),
top_right: BorderRadii::scale_corner_by(self.top_right, s),
bottom_left: BorderRadii::scale_corner_by(self.bottom_left, s),
bottom_right: BorderRadii::scale_corner_by(self.bottom_right, s) }
}
// Scale the border corner radius by the specified factor
pub fn scale_corner_by(corner: Size2D<Au>, s: f32) -> Size2D<Au> {
Size2D { width: corner.width.scale_by(s), height: corner.height.scale_by(s) }
}
} }
impl<T> BorderRadii<T> where T: PartialEq + Zero { impl<T> BorderRadii<T> where T: PartialEq + Zero {
@ -996,10 +1028,10 @@ impl<T> BorderRadii<T> where T: PartialEq + Zero + Clone {
/// Returns a set of border radii that all have the given value. /// Returns a set of border radii that all have the given value.
pub fn all_same(value: T) -> BorderRadii<T> { pub fn all_same(value: T) -> BorderRadii<T> {
BorderRadii { BorderRadii {
top_left: value.clone(), top_left: Size2D { width: value.clone(), height: value.clone() },
top_right: value.clone(), top_right: Size2D { width: value.clone(), height: value.clone() },
bottom_right: value.clone(), bottom_right: Size2D { width: value.clone(), height: value.clone() },
bottom_left: value.clone(), bottom_left: Size2D { width: value.clone(), height: value.clone() },
} }
} }
} }

View file

@ -85,6 +85,13 @@ struct Line {
end: Point2D<f32>, end: Point2D<f32>,
} }
struct CornerOrigin {
top_left: Point2D<f32>,
top_right: Point2D<f32>,
bottom_right: Point2D<f32>,
bottom_left: Point2D<f32>,
}
impl<'a> PaintContext<'a> { impl<'a> PaintContext<'a> {
pub fn draw_target(&self) -> &DrawTarget { pub fn draw_target(&self) -> &DrawTarget {
&self.draw_target &self.draw_target
@ -448,7 +455,7 @@ impl<'a> PaintContext<'a> {
} }
} }
fn ellipse_rightmost_line_intersection_angle(e: Ellipse, l: Line) -> Option<f32> { fn ellipse_rightmost_intersection(e: Ellipse, l: Line) -> Option<f32> {
match PaintContext::ellipse_line_intersection_angles(e, l) { match PaintContext::ellipse_line_intersection_angles(e, l) {
(Some((p0, angle0)), Some((p1, _))) if p0.x > p1.x => Some(angle0), (Some((p0, angle0)), Some((p1, _))) if p0.x > p1.x => Some(angle0),
(_, Some((_, angle1))) => Some(angle1), (_, Some((_, angle1))) => Some(angle1),
@ -457,7 +464,7 @@ impl<'a> PaintContext<'a> {
} }
} }
fn ellipse_leftmost_line_intersection_angle(e: Ellipse, l: Line) -> Option<f32> { fn ellipse_leftmost_intersection(e: Ellipse, l: Line) -> Option<f32> {
match PaintContext::ellipse_line_intersection_angles(e, l) { match PaintContext::ellipse_line_intersection_angles(e, l) {
(Some((p0, angle0)), Some((p1, _))) if p0.x < p1.x => Some(angle0), (Some((p0, angle0)), Some((p1, _))) if p0.x < p1.x => Some(angle0),
(_, Some((_, angle1))) => Some(angle1), (_, Some((_, angle1))) => Some(angle1),
@ -466,6 +473,155 @@ impl<'a> PaintContext<'a> {
} }
} }
fn is_zero_radius(radius: &Size2D<AzFloat>) -> bool {
radius.width <= 0. || radius.height <= 0.
}
// Adapted from gecko:gfx/2d/PathHelpers.h:EllipseToBezier
fn ellipse_to_bezier(path_builder: &mut PathBuilder,
origin: Point2D<AzFloat>,
radius: Size2D<AzFloat>,
start_angle: f32,
end_angle: f32) {
if PaintContext::is_zero_radius(&radius) {
return;
}
// Calculate kappa constant for partial curve. The sign of angle in the
// tangent will actually ensure this is negative for a counter clockwise
// sweep, so changing signs later isn't needed.
let kappa_factor: f32 = (4.0f32 / 3.0f32) * ((end_angle - start_angle) / 4.).tan();
let kappa_x: f32 = kappa_factor * radius.width;
let kappa_y: f32 = kappa_factor * radius.height;
// We guarantee here the current point is the start point of the next
// curve segment.
let start_point = Point2D::new(origin.x + start_angle.cos() * radius.width,
origin.y + start_angle.sin() * radius.height);
path_builder.line_to(start_point);
let end_point = Point2D::new(origin.x + end_angle.cos() * radius.width,
origin.y + end_angle.sin() * radius.height);
let tangent_start = Point2D::new(-start_angle.sin(), start_angle.cos());
let cp1 = Point2D::new(start_point.x + tangent_start.x * kappa_x,
start_point.y + tangent_start.y * kappa_y);
let rev_tangent_end = Point2D::new(end_angle.sin(), -end_angle.cos());
let cp2 = Point2D::new(end_point.x + rev_tangent_end.x * kappa_x,
end_point.y + rev_tangent_end.y * kappa_y);
path_builder.bezier_curve_to(&cp1, &cp2, &end_point);
}
#[allow(non_snake_case)]
fn inner_border_bounds(bounds: &Rect<f32>, border: &SideOffsets2D<f32>) -> Rect<f32> {
// T = top, B = bottom, L = left, R = right
let inner_TL = bounds.origin + Point2D::new(border.left, border.top);
let inner_BR = bounds.bottom_right() + Point2D::new(-border.right, -border.bottom);
Rect::new(inner_TL, Size2D::new(inner_BR.x - inner_TL.x, inner_BR.y - inner_TL.y))
}
#[allow(non_snake_case)]
fn corner_bounds(bounds: &Rect<f32>,
border: &SideOffsets2D<f32>,
radii: &BorderRadii<AzFloat>) -> (CornerOrigin, SideOffsets2D<Size2D<f32>>) {
fn distance_to_elbow(radius: &Size2D<AzFloat>,
corner_width: f32,
corner_height: f32) -> Size2D<f32> {
if corner_width >= radius.width || corner_height >= radius.height {
Size2D::zero()
} else {
Size2D::new(radius.width - corner_width, radius.height - corner_height)
}
}
// T = top, B = bottom, L = left, R = right
let origin_TL = bounds.origin + Point2D::new(radii.top_left.width, radii.top_left.height);
let origin_TR = bounds.top_right() + Point2D::new(-radii.top_right.width,
radii.top_right.height);
let origin_BR = bounds.bottom_right() + Point2D::new(-radii.bottom_right.width,
-radii.bottom_right.height);
let origin_BL = bounds.bottom_left() + Point2D::new(radii.bottom_left.width,
-radii.bottom_left.height);
let elbow_TL = distance_to_elbow(&radii.top_left, border.left, border.top);
let elbow_TR = distance_to_elbow(&radii.top_right, border.right, border.top);
let elbow_BR = distance_to_elbow(&radii.bottom_right, border.right, border.bottom);
let elbow_BL = distance_to_elbow(&radii.bottom_left, border.left, border.bottom);
(CornerOrigin { top_left: origin_TL,
top_right: origin_TR,
bottom_right: origin_BR,
bottom_left: origin_BL },
SideOffsets2D::new(elbow_TL, elbow_TR, elbow_BR, elbow_BL))
}
#[allow(non_snake_case)]
fn draw_corner(path_builder: &mut PathBuilder,
origin: &Point2D<AzFloat>,
radius: &Size2D<AzFloat>,
inner_border: &Point2D<AzFloat>,
outer_border: &Point2D<AzFloat>,
dist_elbow: &Size2D<AzFloat>,
clockwise: bool) {
let rad_R: AzFloat = 0.;
let rad_BR = rad_R + f32::consts::FRAC_PI_4;
let rad_B = rad_BR + f32::consts::FRAC_PI_4;
let rad_BL = rad_B + f32::consts::FRAC_PI_4;
let rad_L = rad_BL + f32::consts::FRAC_PI_4;
let rad_TL = rad_L + f32::consts::FRAC_PI_4;
let rad_T = rad_TL + f32::consts::FRAC_PI_4;
fn compatible_borders_corner(border_corner_radius: &Size2D<f32>,
border1_width: f32,
border2_width: f32) -> bool {
(border_corner_radius.width - border_corner_radius.height).abs() <= f32::EPSILON &&
(border1_width - border2_width).abs() <= f32::EPSILON
}
if PaintContext::is_zero_radius(radius) {
return;
}
let ellipse = Ellipse { origin: *origin, width: radius.width, height: radius.height };
let simple_border = compatible_borders_corner(&radius,
(outer_border.x - inner_border.x).abs(),
(outer_border.y - inner_border.y).abs());
let corner_angle = if simple_border {
f32::consts::FRAC_PI_4
} else {
if inner_border.x >= outer_border.x {
PaintContext::ellipse_leftmost_intersection(ellipse,
Line { start: *outer_border,
end: *inner_border }).unwrap()
} else {
PaintContext::ellipse_rightmost_intersection(ellipse,
Line { start: *inner_border,
end: *outer_border }).unwrap()
}
};
let (start_angle, end_angle) = match (inner_border.x <= outer_border.x,
inner_border.y >= outer_border.y) {
// TR corner - top border & right border
(true, true) => if clockwise { (-rad_B, rad_R - corner_angle) } else { (rad_R - corner_angle, rad_R) },
// BR corner - right border & bottom border
(true, false) => if clockwise { (rad_R, rad_R + corner_angle) } else { (rad_R + corner_angle, rad_B) },
// TL corner - left border & top border
(false, true) => if clockwise { (rad_L, rad_L + corner_angle) } else { (rad_L + corner_angle, rad_T) },
// BL corner - bottom border & left border
(false, false) => if clockwise { (rad_B, rad_L - corner_angle) } else { (rad_L - corner_angle, rad_L) },
};
if clockwise {
PaintContext::ellipse_to_bezier(path_builder, *origin, *radius, start_angle, end_angle);
PaintContext::ellipse_to_bezier(path_builder, *origin, *dist_elbow, end_angle, start_angle);
} else {
PaintContext::ellipse_to_bezier(path_builder, *origin, *dist_elbow, end_angle, start_angle);
PaintContext::ellipse_to_bezier(path_builder, *origin, *radius, start_angle, end_angle);
}
}
// The following comment is wonderful, and stolen from // The following comment is wonderful, and stolen from
// gecko:gfx/thebes/gfxContext.cpp:RoundedRectangle for reference. // gecko:gfx/thebes/gfxContext.cpp:RoundedRectangle for reference.
// //
@ -552,27 +708,17 @@ impl<'a> PaintContext<'a> {
bounds: &Rect<f32>, bounds: &Rect<f32>,
direction: Direction, direction: Direction,
border: &SideOffsets2D<f32>, border: &SideOffsets2D<f32>,
radius: &BorderRadii<AzFloat>, radii: &BorderRadii<AzFloat>,
mode: BorderPathDrawingMode) { mode: BorderPathDrawingMode) {
// T = top, B = bottom, L = left, R = right // T = top, B = bottom, L = left, R = right
let inner = PaintContext::inner_border_bounds(bounds, &border);
let box_TL = bounds.origin; let (box_TL, inner_TL,
let box_TR = box_TL + Point2D::new(bounds.size.width, 0.0); box_TR, inner_TR,
let box_BL = box_TL + Point2D::new(0.0, bounds.size.height); box_BR, inner_BR,
let box_BR = box_TL + Point2D::new(bounds.size.width, bounds.size.height); box_BL, inner_BL) = (bounds.origin, inner.origin,
bounds.top_right(), inner.top_right(),
let inner_TL = box_TL + Point2D::new(border.left, border.top); bounds.bottom_right(), inner.bottom_right(),
let inner_TR = box_TR + Point2D::new(-border.right, border.top); bounds.bottom_left(), inner.bottom_left());
let inner_BR = box_BR + Point2D::new(-border.right, -border.bottom);
let inner_BL = box_BL + Point2D::new(border.left, -border.bottom);
let rad_R: AzFloat = 0.;
let rad_BR = rad_R + f32::consts::FRAC_PI_4;
let rad_B = rad_BR + f32::consts::FRAC_PI_4;
let rad_BL = rad_B + f32::consts::FRAC_PI_4;
let rad_L = rad_BL + f32::consts::FRAC_PI_4;
let rad_TL = rad_L + f32::consts::FRAC_PI_4;
let rad_T = rad_TL + f32::consts::FRAC_PI_4;
fn dx(x: AzFloat) -> Point2D<AzFloat> { fn dx(x: AzFloat) -> Point2D<AzFloat> {
Point2D::new(x, 0.) Point2D::new(x, 0.)
@ -590,44 +736,23 @@ impl<'a> PaintContext<'a> {
Point2D::new(0., if cond { dy } else { 0. }) Point2D::new(0., if cond { dy } else { 0. })
} }
fn compatible_borders_corner(border1_width: f32, border2_width: f32) -> bool { let (corner_origin, elbow) =
(border1_width - border2_width).abs() <= f32::EPSILON PaintContext::corner_bounds(bounds, border, radii);
}
let distance_to_elbow_TL = let (elbow_TL, elbow_TR, elbow_BR, elbow_BL) =
if border.top == border.left { (elbow.top, elbow.right, elbow.bottom, elbow.left);
(radius.top_left - border.top).max(0.)
} else {
0.
};
let distance_to_elbow_TR =
if border.top == border.right {
(radius.top_right - border.top).max(0.)
} else {
0.
};
let distance_to_elbow_BR =
if border.right == border.bottom {
(radius.bottom_right - border.bottom).max(0.)
} else {
0.
};
let distance_to_elbow_BL =
if border.left == border.bottom {
(radius.bottom_left - border.bottom).max(0.)
} else {
0.
};
match direction { match direction {
Direction::Top => { Direction::Top => {
let edge_TL = box_TL + dx(radius.top_left.max(border.left)); let edge_TL = box_TL + dx(radii.top_left.width.max(border.left));
let edge_TR = box_TR + dx(-radius.top_right.max(border.right)); let edge_TR = box_TR + dx(-radii.top_right.width.max(border.right));
let edge_BR = box_TR + dx(-border.right - distance_to_elbow_TR) + dy(border.top); let edge_BR = box_TR + dx(-border.right - elbow_TR.width) + dy(border.top);
let edge_BL = box_TL + dx(border.left + distance_to_elbow_TL) + dy(border.top); let edge_BL = box_TL + dx(border.left + elbow_TL.width) + dy(border.top);
let corner_TL = edge_TL + dx_if(radius.top_left == 0., -border.left); let corner_TL = edge_TL + dx_if(PaintContext::is_zero_radius(&radii.top_left),
let corner_TR = edge_TR + dx_if(radius.top_right == 0., border.right); -border.left);
let corner_TR = edge_TR + dx_if(PaintContext::is_zero_radius(&radii.top_right),
border.right);
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
@ -636,25 +761,13 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_TR), BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_TR),
} }
PaintContext::draw_corner(path_builder,
if radius.top_right != 0. { &corner_origin.top_right,
// the origin is the center of the arcs we're about to draw. &radii.top_right,
let origin = edge_TR + Point2D::new((border.right - radius.top_right).max(0.), &inner_TR,
radius.top_right); &box_TR,
let angle = if compatible_borders_corner(border.top, border.right) { &elbow_TR,
f32::consts::FRAC_PI_4 true);
} else {
let line = Line { start: inner_TR, end: box_TR };
let ellipse = Ellipse { origin: origin, width: radius.top_right, height: radius.top_right };
PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap()
};
path_builder.arc(origin, radius.top_right, rad_T, rad_R - angle, false);
if distance_to_elbow_TR != 0. {
path_builder.arc(origin, distance_to_elbow_TR, rad_R - angle, rad_T, true);
}
}
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
path_builder.line_to(edge_BR); path_builder.line_to(edge_BR);
@ -662,32 +775,25 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_BL), BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_BL),
} }
PaintContext::draw_corner(path_builder,
if radius.top_left != 0. { &corner_origin.top_left,
let origin = edge_TL + Point2D::new(-(border.left - radius.top_left).max(0.), &radii.top_left,
radius.top_left); &inner_TL,
let angle = if compatible_borders_corner(border.top, border.left) { &box_TL,
f32::consts::FRAC_PI_4 &elbow_TL,
} else { false);
let line = Line { start: box_TL, end: inner_TL };
let ellipse = Ellipse { origin: origin, width: radius.top_left, height: radius.top_left };
PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap()
};
if distance_to_elbow_TL != 0. {
path_builder.arc(origin, distance_to_elbow_TL, rad_T, rad_L + angle, true);
}
path_builder.arc(origin, radius.top_left, rad_L + angle, rad_T, false);
}
} }
Direction::Left => { Direction::Left => {
let edge_TL = box_TL + dy(radius.top_left.max(border.top)); let edge_TL = box_TL + dy(radii.top_left.height.max(border.top));
let edge_BL = box_BL + dy(-radius.bottom_left.max(border.bottom)); let edge_BL = box_BL + dy(-radii.bottom_left.height.max(border.bottom));
let edge_TR = box_TL + dx(border.left) + dy(border.top + distance_to_elbow_TL); let edge_TR = box_TL + dx(border.left) + dy(border.top + elbow_TL.height);
let edge_BR = box_BL + dx(border.left) + dy(-border.bottom - distance_to_elbow_BL); let edge_BR = box_BL + dx(border.left) + dy(-border.bottom -
elbow_BL.height);
let corner_TL = edge_TL + dy_if(radius.top_left == 0., -border.top); let corner_TL = edge_TL + dy_if(PaintContext::is_zero_radius(&radii.top_left),
let corner_BL = edge_BL + dy_if(radius.bottom_left == 0., border.bottom); -border.top);
let corner_BL = edge_BL + dy_if(PaintContext::is_zero_radius(&radii.bottom_left),
border.bottom);
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
@ -696,25 +802,13 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_TL), BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_TL),
} }
PaintContext::draw_corner(path_builder,
if radius.top_left != 0. { &corner_origin.top_left,
let origin = edge_TL + Point2D::new(radius.top_left, &radii.top_left,
-(border.top - radius.top_left).max(0.)); &inner_TL,
&box_TL,
let angle = if compatible_borders_corner(border.top, border.left) { &elbow_TL,
f32::consts::FRAC_PI_4 true);
} else {
let line = Line { start: box_TL, end: inner_TL };
let ellipse = Ellipse { origin: origin, width: radius.top_left, height: radius.top_left };
PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap()
};
path_builder.arc(origin, radius.top_left, rad_L, rad_L + angle, false);
if distance_to_elbow_TL != 0. {
path_builder.arc(origin, distance_to_elbow_TL, rad_L + angle, rad_L, true);
}
}
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
path_builder.line_to(edge_TR); path_builder.line_to(edge_TR);
@ -722,35 +816,25 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_BR), BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_BR),
} }
PaintContext::draw_corner(path_builder,
if radius.bottom_left != 0. { &corner_origin.bottom_left,
let origin = edge_BL + &radii.bottom_left,
Point2D::new(radius.bottom_left, &inner_BL,
(border.bottom - radius.bottom_left).max(0.)); &box_BL,
let angle = if compatible_borders_corner(border.bottom, border.left) { &elbow_BL,
f32::consts::FRAC_PI_4 false);
} else {
let line = Line { start: box_BL, end: inner_BL };
let ellipse = Ellipse { origin: origin,
width: radius.bottom_left,
height: radius.bottom_left };
PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap()
};
if distance_to_elbow_BL != 0. {
path_builder.arc(origin, distance_to_elbow_BL, rad_L, rad_L - angle, true);
}
path_builder.arc(origin, radius.bottom_left, rad_L - angle, rad_L, false);
}
} }
Direction::Right => { Direction::Right => {
let edge_TR = box_TR + dy(radius.top_right.max(border.top)); let edge_TR = box_TR + dy(radii.top_right.height.max(border.top));
let edge_BR = box_BR + dy(-radius.bottom_right.max(border.bottom)); let edge_BR = box_BR + dy(-radii.bottom_right.height.max(border.bottom));
let edge_TL = box_TR + dx(-border.right) + dy(border.top + distance_to_elbow_TR); let edge_TL = box_TR + dx(-border.right) + dy(border.top + elbow_TR.height);
let edge_BL = box_BR + dx(-border.right) + dy(-border.bottom - distance_to_elbow_BR); let edge_BL = box_BR + dx(-border.right) + dy(-border.bottom -
elbow_BR.height);
let corner_TR = edge_TR + dy_if(radius.top_right == 0., -border.top); let corner_TR = edge_TR + dy_if(PaintContext::is_zero_radius(&radii.top_right),
let corner_BR = edge_BR + dy_if(radius.bottom_right == 0., border.bottom); -border.top);
let corner_BR = edge_BR + dy_if(PaintContext::is_zero_radius(&radii.bottom_right),
border.bottom);
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
@ -759,24 +843,13 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_TL), BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_TL),
} }
PaintContext::draw_corner(path_builder,
if radius.top_right != 0. { &corner_origin.top_right,
let origin = edge_TR + Point2D::new(-radius.top_right, &radii.top_right,
-(border.top - radius.top_right).max(0.)); &inner_TR,
let angle = if compatible_borders_corner(border.top, border.right) { &box_TR,
f32::consts::FRAC_PI_4 &elbow_TR,
} else { false);
let line = Line { start: inner_TR, end: box_TR };
let ellipse = Ellipse { origin: origin, width: radius.top_right, height: radius.top_right };
PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap()
};
if distance_to_elbow_TR != 0. {
path_builder.arc(origin, distance_to_elbow_TR, rad_R, rad_R - angle, true);
}
path_builder.arc(origin, radius.top_right, rad_R - angle, rad_R, false);
}
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
path_builder.line_to(corner_TR); path_builder.line_to(corner_TR);
@ -784,35 +857,27 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_BR), BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_BR),
} }
PaintContext::draw_corner(path_builder,
&corner_origin.bottom_right,
&radii.bottom_right,
&inner_BR,
&box_BR,
&elbow_BR,
true);
if radius.bottom_right != 0. {
let origin = edge_BR +
Point2D::new(-radius.bottom_right,
(border.bottom - radius.bottom_right).max(0.));
let angle = if compatible_borders_corner(border.bottom, border.right) {
f32::consts::FRAC_PI_4
} else {
let line = Line { start: inner_BR, end: box_BR };
let ellipse = Ellipse { origin: origin,
width: radius.bottom_right,
height: radius.bottom_right };
PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap()
};
path_builder.arc(origin, radius.bottom_right, rad_R, rad_R + angle, false);
if distance_to_elbow_BR != 0. {
path_builder.arc(origin, distance_to_elbow_BR, rad_R + angle, rad_R, true);
}
}
} }
Direction::Bottom => { Direction::Bottom => {
let edge_BL = box_BL + dx(radius.bottom_left.max(border.left)); let edge_BL = box_BL + dx(radii.bottom_left.width.max(border.left));
let edge_BR = box_BR + dx(-radius.bottom_right.max(border.right)); let edge_BR = box_BR + dx(-radii.bottom_right.width.max(border.right));
let edge_TL = box_BL + dy(-border.bottom) + dx(border.left + distance_to_elbow_BL); let edge_TL = box_BL + dy(-border.bottom) + dx(border.left +
let edge_TR = box_BR + dy(-border.bottom) + dx(-border.right - distance_to_elbow_BR); elbow_BL.width);
let edge_TR = box_BR + dy(-border.bottom) + dx(-border.right -
elbow_BR.width);
let corner_BR = edge_BR + dx_if(radius.bottom_right == 0., border.right); let corner_BR = edge_BR + dx_if(PaintContext::is_zero_radius(&radii.bottom_right),
let corner_BL = edge_BL + dx_if(radius.bottom_left == 0., -border.left); border.right);
let corner_BL = edge_BL + dx_if(PaintContext::is_zero_radius(&radii.bottom_left),
-border.left);
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
@ -821,26 +886,13 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_TR), BorderPathDrawingMode::CornersOnly => path_builder.move_to(edge_TR),
} }
PaintContext::draw_corner(path_builder,
if radius.bottom_right != 0. { &corner_origin.bottom_right,
let origin = edge_BR + Point2D::new((border.right - radius.bottom_right).max(0.), &radii.bottom_right,
-radius.bottom_right); &inner_BR,
let angle = if compatible_borders_corner(border.bottom, border.right) { &box_BR,
f32::consts::FRAC_PI_4 &elbow_BR,
} else { false);
let line = Line { start: inner_BR, end: box_BR };
let ellipse = Ellipse { origin: origin,
width: radius.bottom_right,
height: radius.bottom_right };
PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap()
};
if distance_to_elbow_BR != 0. {
path_builder.arc(origin, distance_to_elbow_BR, rad_B, rad_R + angle, true);
}
path_builder.arc(origin, radius.bottom_right, rad_R + angle, rad_B, false);
}
match mode { match mode {
BorderPathDrawingMode::EntireBorder => { BorderPathDrawingMode::EntireBorder => {
path_builder.line_to(corner_BR); path_builder.line_to(corner_BR);
@ -848,25 +900,13 @@ impl<'a> PaintContext<'a> {
} }
BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_BL), BorderPathDrawingMode::CornersOnly => path_builder.move_to(corner_BL),
} }
PaintContext::draw_corner(path_builder,
if radius.bottom_left != 0. { &corner_origin.bottom_left,
let origin = edge_BL - Point2D::new((border.left - radius.bottom_left).max(0.), &radii.bottom_left,
radius.bottom_left); &inner_BL,
let angle = if compatible_borders_corner(border.bottom, border.left) { &box_BL,
f32::consts::FRAC_PI_4 &elbow_BL,
} else { true);
let line = Line { start: box_BL, end: inner_BL };
let ellipse = Ellipse { origin: origin,
width: radius.bottom_left,
height: radius.bottom_left };
PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap()
};
path_builder.arc(origin, radius.bottom_left, rad_B, rad_L - angle, false);
if distance_to_elbow_BL != 0. {
path_builder.arc(origin, distance_to_elbow_BL, rad_L - angle, rad_B, true);
}
}
} }
} }
} }
@ -879,6 +919,7 @@ impl<'a> PaintContext<'a> {
/// slice through the rounded corners. My first attempt to unify with the above code resulted /// slice through the rounded corners. My first attempt to unify with the above code resulted
/// in making a mess of it, and the simplicity of this code path is appealing, so it may not /// in making a mess of it, and the simplicity of this code path is appealing, so it may not
/// be worth it… In any case, revisit this decision when we support elliptical radii. /// be worth it… In any case, revisit this decision when we support elliptical radii.
#[allow(non_snake_case)]
fn create_rounded_rect_path(&self, fn create_rounded_rect_path(&self,
path_builder: &mut PathBuilder, path_builder: &mut PathBuilder,
bounds: &Rect<f32>, bounds: &Rect<f32>,
@ -890,36 +931,93 @@ impl<'a> PaintContext<'a> {
// + 7 4 + // + 7 4 +
// \ 6 5 / // \ 6 5 /
// +----------+ // +----------+
let border = SideOffsets2D::new(radii.top_left.height.max(radii.top_right.height),
radii.bottom_right.width.max(radii.top_right.width),
radii.bottom_right.height.max(radii.bottom_left.height),
radii.top_left.height.max(radii.bottom_left.height));
path_builder.move_to(Point2D::new(bounds.origin.x + radii.top_left, bounds.origin.y)); // 1 // T = top, B = bottom, L = left, R = right
path_builder.line_to(Point2D::new(bounds.max_x() - radii.top_right, bounds.origin.y)); // 2 let inner = PaintContext::inner_border_bounds(bounds, &border);
path_builder.arc(Point2D::new(bounds.max_x() - radii.top_right, let (outer_TL, inner_TL,
bounds.origin.y + radii.top_right), outer_TR, inner_TR,
radii.top_right, outer_BR, inner_BR,
1.5f32 * f32::consts::FRAC_PI_2, outer_BL, inner_BL) = (bounds.origin, inner.origin,
2.0f32 * f32::consts::PI, bounds.top_right(), inner.top_right(),
false); // 3 bounds.bottom_right(), inner.bottom_right(),
path_builder.line_to(Point2D::new(bounds.max_x(), bounds.max_y() - radii.bottom_right)); // 4 bounds.bottom_left(), inner.bottom_left());
path_builder.arc(Point2D::new(bounds.max_x() - radii.bottom_right,
bounds.max_y() - radii.bottom_right), let (corner_origin, _) =
radii.bottom_right, PaintContext::corner_bounds(bounds, &border, radii);
0.0, let (origin_TL, origin_TR, origin_BR, origin_BL) = (corner_origin.top_left,
f32::consts::FRAC_PI_2, corner_origin.top_right,
false); // 5 corner_origin.bottom_right,
path_builder.line_to(Point2D::new(bounds.origin.x + radii.bottom_left, bounds.max_y())); // 6 corner_origin.bottom_left);
path_builder.arc(Point2D::new(bounds.origin.x + radii.bottom_left, let zero_elbow = Size2D::new(0., 0.);
bounds.max_y() - radii.bottom_left),
radii.bottom_left, path_builder.move_to(Point2D::new(origin_TL.x - radii.top_left.width, origin_TL.y));
f32::consts::FRAC_PI_2, path_builder.move_to(Point2D::new(bounds.origin.x + radii.top_left.width, bounds.origin.y)); // 1
f32::consts::PI, path_builder.line_to(Point2D::new(bounds.max_x() - radii.top_right.width, bounds.origin.y)); // 2
false); // 7 PaintContext::draw_corner(path_builder, // 3
path_builder.line_to(Point2D::new(bounds.origin.x, bounds.origin.y + radii.top_left)); // 8 &origin_TR,
path_builder.arc(Point2D::new(bounds.origin.x + radii.top_left, &radii.top_right,
bounds.origin.y + radii.top_left), &inner_TR,
radii.top_left, &outer_TR,
f32::consts::PI, &zero_elbow,
1.5f32 * f32::consts::FRAC_PI_2, true);
false); // 1 PaintContext::draw_corner(path_builder, // 3
&origin_TR,
&radii.top_right,
&inner_TR,
&outer_TR,
&zero_elbow,
false);
path_builder.line_to(Point2D::new(bounds.max_x(), bounds.max_y() - radii.bottom_right.width)); // 4
PaintContext::draw_corner(path_builder, // 5
&origin_BR,
&radii.bottom_right,
&inner_BR,
&outer_BR,
&zero_elbow,
true);
PaintContext::draw_corner(path_builder, // 5
&origin_BR,
&radii.bottom_right,
&inner_BR,
&outer_BR,
&zero_elbow,
false);
path_builder.line_to(Point2D::new(bounds.origin.x + radii.bottom_left.width,
bounds.max_y())); // 6
PaintContext::draw_corner(path_builder, // 7
&origin_BL,
&radii.bottom_left,
&inner_BL,
&outer_BL,
&zero_elbow,
true);
PaintContext::draw_corner(path_builder, // 7
&origin_BL,
&radii.bottom_left,
&inner_BL,
&outer_BL,
&zero_elbow,
false);
path_builder.line_to(Point2D::new(bounds.origin.x,
bounds.origin.y + radii.top_left.height)); // 8
PaintContext::draw_corner(path_builder, // 9
&origin_TL,
&radii.top_left,
&inner_TL,
&outer_TL,
&zero_elbow,
true);
PaintContext::draw_corner(path_builder, // 9
&origin_TL,
&radii.top_left,
&inner_TL,
&outer_TL,
&zero_elbow,
false);
} }
fn draw_dashed_border_segment(&self, fn draw_dashed_border_segment(&self,
@ -947,26 +1045,26 @@ impl<'a> PaintContext<'a> {
let (start, end) = match direction { let (start, end) = match direction {
Direction::Top => { Direction::Top => {
let y = rect.origin.y + border.top * 0.5; let y = rect.origin.y + border.top * 0.5;
let start = Point2D::new(rect.origin.x + radius.top_left, y); let start = Point2D::new(rect.origin.x + radius.top_left.width, y);
let end = Point2D::new(rect.origin.x + rect.size.width - radius.top_right, y); let end = Point2D::new(rect.origin.x + rect.size.width - radius.top_right.width, y);
(start, end) (start, end)
} }
Direction::Left => { Direction::Left => {
let x = rect.origin.x + border.left * 0.5; let x = rect.origin.x + border.left * 0.5;
let start = Point2D::new(x, rect.origin.y + rect.size.height - radius.bottom_left); let start = Point2D::new(x, rect.origin.y + rect.size.height - radius.bottom_left.height);
let end = Point2D::new(x, rect.origin.y + border.top.max(radius.top_left)); let end = Point2D::new(x, rect.origin.y + border.top.max(radius.top_left.height));
(start, end) (start, end)
} }
Direction::Right => { Direction::Right => {
let x = rect.origin.x + rect.size.width - border.right * 0.5; let x = rect.origin.x + rect.size.width - border.right * 0.5;
let start = Point2D::new(x, rect.origin.y + radius.top_right); let start = Point2D::new(x, rect.origin.y + radius.top_right.height);
let end = Point2D::new(x, rect.origin.y + rect.size.height - radius.bottom_right); let end = Point2D::new(x, rect.origin.y + rect.size.height - radius.bottom_right.height);
(start, end) (start, end)
} }
Direction::Bottom => { Direction::Bottom => {
let y = rect.origin.y + rect.size.height - border.bottom * 0.5; let y = rect.origin.y + rect.size.height - border.bottom * 0.5;
let start = Point2D::new(rect.origin.x + rect.size.width - radius.bottom_right, y); let start = Point2D::new(rect.origin.x + rect.size.width - radius.bottom_right.width, y);
let end = Point2D::new(rect.origin.x + border.left.max(radius.bottom_left), y); let end = Point2D::new(rect.origin.x + border.left.max(radius.bottom_left.width), y);
(start, end) (start, end)
} }
}; };
@ -1532,11 +1630,15 @@ impl ToRadiiPx for BorderRadii<Au> {
} }
BorderRadii { BorderRadii {
top_left: to_nearest_px(self.top_left), top_left: Size2D { width: to_nearest_px(self.top_left.width),
top_right: to_nearest_px(self.top_right), height: to_nearest_px(self.top_left.height) },
bottom_left: to_nearest_px(self.bottom_left), top_right: Size2D { width: to_nearest_px(self.top_right.width),
bottom_right: to_nearest_px(self.bottom_right), height: to_nearest_px(self.top_right.height) },
} bottom_left: Size2D { width: to_nearest_px(self.bottom_left.width),
height: to_nearest_px(self.bottom_left.height) },
bottom_right: Size2D { width: to_nearest_px(self.bottom_right.width),
height: to_nearest_px(self.bottom_right.height) },
}
} }
} }
@ -1769,11 +1871,14 @@ enum BorderPathDrawingMode {
} }
fn radii_apply_to_border_direction(direction: Direction, radius: &BorderRadii<AzFloat>) -> bool { fn radii_apply_to_border_direction(direction: Direction, radius: &BorderRadii<AzFloat>) -> bool {
match (direction, radius.top_left, radius.top_right, radius.bottom_left, radius.bottom_right) { match (direction,
radius.top_left.width,
radius.top_right.width,
radius.bottom_left.width,
radius.bottom_right.width) {
(Direction::Top, a, b, _, _) | (Direction::Top, a, b, _, _) |
(Direction::Right, _, a, _, b) | (Direction::Right, _, a, _, b) |
(Direction::Bottom, _, _, a, b) | (Direction::Bottom, _, _, a, b) |
(Direction::Left, a, _, b, _) => a != 0.0 || b != 0.0, (Direction::Left, a, _, b, _) => a != 0.0 || b != 0.0,
} }
} }

View file

@ -280,18 +280,13 @@ fn handle_overlapping_radii(size: &Size2D<Au>, radii: &BorderRadii<Au>) -> Borde
} }
} }
let top_factor = scale_factor(radii.top_left, radii.top_right, size.width); let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, size.width);
let bottom_factor = scale_factor(radii.bottom_left, radii.bottom_right, size.width); let bottom_factor = scale_factor(radii.bottom_left.width, radii.bottom_right.width, size.width);
let left_factor = scale_factor(radii.top_left, radii.bottom_left, size.height); let left_factor = scale_factor(radii.top_left.height, radii.bottom_left.height, size.height);
let right_factor = scale_factor(radii.top_right, radii.bottom_right, size.height); let right_factor = scale_factor(radii.top_right.height, radii.bottom_right.height, size.height);
let min_factor = top_factor.min(bottom_factor).min(left_factor).min(right_factor); let min_factor = top_factor.min(bottom_factor).min(left_factor).min(right_factor);
if min_factor < 1.0 { if min_factor < 1.0 {
BorderRadii { radii.scale_by(min_factor)
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 { } else {
*radii *radii
} }
@ -303,14 +298,14 @@ fn build_border_radius(abs_bounds: &Rect<Au>, border_style: &Border) -> BorderRa
// radii will be relative to the width. // radii will be relative to the width.
handle_overlapping_radii(&abs_bounds.size, &BorderRadii { handle_overlapping_radii(&abs_bounds.size, &BorderRadii {
top_left: model::specified(border_style.border_top_left_radius, top_left: model::specified_border_radius(border_style.border_top_left_radius,
abs_bounds.size.width), abs_bounds.size.width),
top_right: model::specified(border_style.border_top_right_radius, top_right: model::specified_border_radius(border_style.border_top_right_radius,
abs_bounds.size.width), abs_bounds.size.width),
bottom_right: model::specified(border_style.border_bottom_right_radius, bottom_right: model::specified_border_radius(border_style.border_bottom_right_radius,
abs_bounds.size.width), abs_bounds.size.width),
bottom_left: model::specified(border_style.border_bottom_left_radius, bottom_left: model::specified_border_radius(border_style.border_bottom_left_radius,
abs_bounds.size.width), abs_bounds.size.width),
}) })
} }

View file

@ -8,12 +8,12 @@
use fragment::Fragment; use fragment::Fragment;
use euclid::{Matrix4, SideOffsets2D}; use euclid::{Matrix4, SideOffsets2D, Size2D};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::fmt; use std::fmt;
use style::computed_values::transform::ComputedMatrix; use style::computed_values::transform::ComputedMatrix;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::LengthOrPercentageOrAuto; use style::values::computed::{BorderRadiusSize, LengthOrPercentageOrAuto};
use style::values::computed::{LengthOrPercentageOrNone, LengthOrPercentage}; use style::values::computed::{LengthOrPercentageOrNone, LengthOrPercentage};
use util::geometry::Au; use util::geometry::Au;
use util::logical_geometry::LogicalMargin; use util::logical_geometry::LogicalMargin;
@ -425,6 +425,13 @@ pub fn specified(length: LengthOrPercentage, containing_length: Au) -> Au {
} }
} }
pub fn specified_border_radius(radius: BorderRadiusSize, containing_length: Au) -> Size2D<Au> {
let BorderRadiusSize(size) = radius;
let w = specified(size.width, containing_length);
let h = specified(size.height, containing_length);
Size2D::new(w, h)
}
#[inline] #[inline]
pub fn padding_from_style(style: &ComputedValues, containing_block_inline_size: Au) pub fn padding_from_style(style: &ComputedValues, containing_block_inline_size: Au)
-> LogicalMargin<Au> { -> LogicalMargin<Au> {

View file

@ -377,9 +377,9 @@ pub mod longhands {
// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage> // FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]: % for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
${predefined_type("border-" + corner + "-radius", "LengthOrPercentage", ${predefined_type("border-" + corner + "-radius", "BorderRadiusSize",
"computed::LengthOrPercentage::Length(Au(0))", "computed::BorderRadiusSize::zero()",
"parse_non_negative")} "parse")}
% endfor % endfor
${new_style_struct("Outline", is_inherited=False)} ${new_style_struct("Outline", is_inherited=False)}
@ -5135,16 +5135,16 @@ pub mod shorthands {
'border-%s-radius' % (corner) 'border-%s-radius' % (corner)
for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left'] for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
)}"> )}">
use util::geometry::Au; use values::specified::BorderRadiusSize;
use values::specified::{Length, LengthOrPercentage};
let _ignored = context; let _ignored = context;
fn parse_one_set_of_border_radii(mut input: &mut Parser) fn parse_one_set_of_border_radii(mut input: &mut Parser)
-> Result<[LengthOrPercentage; 4], ()> { -> Result<[BorderRadiusSize; 4], ()> {
let mut count = 0; let mut count = 0;
let mut values = [LengthOrPercentage::Length(Length::Absolute(Au(0))); 4]; let mut values = [BorderRadiusSize::zero(); 4];
while count < 4 { while count < 4 {
if let Ok(value) = input.try(LengthOrPercentage::parse) { if let Ok(value) = input.try(BorderRadiusSize::parse_one_radii) {
values[count] = value; values[count] = value;
count += 1; count += 1;
} else { } else {
@ -5162,7 +5162,7 @@ pub mod shorthands {
} }
let radii = try!(parse_one_set_of_border_radii(input)); let radii = try!(parse_one_set_of_border_radii(input));
// TODO(pcwalton): Elliptical borders. // TODO(bjwbell): Finish parsing code for elliptical borders.
Ok(Longhands { Ok(Longhands {
border_top_left_radius: Some(radii[0]), border_top_left_radius: Some(radii[0]),

View file

@ -834,6 +834,46 @@ pub mod specified {
} }
} }
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
impl BorderRadiusSize {
pub fn zero() -> BorderRadiusSize {
let zero = LengthOrPercentage::Length(Length::Absolute(Au(0)));
BorderRadiusSize(Size2D::new(zero, zero))
}
}
impl ToCss for BorderRadiusSize {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let BorderRadiusSize(size) = *self;
try!(size.width.to_css(dest));
try!(dest.write_str(" "));
size.height.to_css(dest)
}
}
impl BorderRadiusSize {
pub fn circle(radius: LengthOrPercentage) -> BorderRadiusSize {
BorderRadiusSize(Size2D::new(radius, radius))
}
pub fn parse_one_radii(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
if let Ok(first) = LengthOrPercentage::parse_non_negative(input) {
Ok(BorderRadiusSize(Size2D::new(first, first)))
} else {
Err(())
}
}
#[allow(dead_code)]
#[inline]
pub fn parse(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
let first = try!(LengthOrPercentage::parse_non_negative(input));
let second = input.try(LengthOrPercentage::parse_non_negative).unwrap_or(first);
Ok(BorderRadiusSize(Size2D::new(first, second)))
}
}
// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position // http://dev.w3.org/csswg/css2/colors.html#propdef-background-position
#[derive(Clone, PartialEq, Copy)] #[derive(Clone, PartialEq, Copy)]
pub enum PositionComponent { pub enum PositionComponent {
@ -1091,6 +1131,22 @@ pub mod specified {
} }
} }
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
input.try(BorderRadiusSize::parse).or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
"thin" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(1.)))),
"medium" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(3.)))),
"thick" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(5.))))
_ => Err(())
}
})
}
pub fn parse_border_width(input: &mut Parser) -> Result<Length, ()> { pub fn parse_border_width(input: &mut Parser) -> Result<Length, ()> {
input.try(Length::parse_non_negative).or_else(|()| { input.try(Length::parse_non_negative).or_else(|()| {
@ -1302,6 +1358,36 @@ pub mod computed {
} }
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
impl BorderRadiusSize {
pub fn zero() -> BorderRadiusSize {
BorderRadiusSize(Size2D::new(LengthOrPercentage::Length(Au(0)), LengthOrPercentage::Length(Au(0))))
}
}
impl ToComputedValue for specified::BorderRadiusSize {
type ComputedValue = BorderRadiusSize;
#[inline]
fn to_computed_value(&self, context: &Context) -> BorderRadiusSize {
let specified::BorderRadiusSize(s) = *self;
let w = s.width.to_computed_value(context);
let h = s.height.to_computed_value(context);
BorderRadiusSize(Size2D::new(w, h))
}
}
impl ::cssparser::ToCss for BorderRadiusSize {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let BorderRadiusSize(s) = *self;
try!(s.width.to_css(dest));
try!(dest.write_str("/"));
s.height.to_css(dest)
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)] #[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrPercentage { pub enum LengthOrPercentage {
Length(Au), Length(Au),

View file

@ -65,6 +65,7 @@ flaky_cpu == append_style_a.html append_style_b.html
== border_radius_asymmetric_sizes_a.html border_radius_asymmetric_sizes_ref.html == border_radius_asymmetric_sizes_a.html border_radius_asymmetric_sizes_ref.html
== border_radius_clip_a.html border_radius_clip_ref.html == border_radius_clip_a.html border_radius_clip_ref.html
!= border_radius_dashed_a.html border_radius_dashed_ref.html != border_radius_dashed_a.html border_radius_dashed_ref.html
== border_radius_elliptical_a.html border_radius_elliptical_ref.html
== border_radius_overlapping_a.html border_radius_overlapping_ref.html == border_radius_overlapping_a.html border_radius_overlapping_ref.html
== border_spacing_a.html border_spacing_ref.html == border_spacing_a.html border_spacing_ref.html
== border_spacing_auto_layout_a.html border_spacing_ref.html == border_spacing_auto_layout_a.html border_spacing_ref.html

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<style type="text/css">
div.box {
background: white;
border-width: 15px 15px 15px 15px;
border-color: yellow red green blue;
border-style: solid;
border-top-left-radius: 100px 100px;
border-top-right-radius: 200px 100px;
border-bottom-right-radius: 100px 200px;
border-bottom-left-radius: 200px 200px;
height: 500px;
width: 500px;
}
</style>
</head>
<body>
<h2>Border Radius - Elliptical</h2>
<div id="box1" class="box top"></div><br>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<style type="text/css">
#box1-top {
background: white;
border-width: 15px 15px 0px 15px;
border-color: yellow red green blue;
border-style: solid;
border-top-left-radius: 100px 100px;
border-top-right-radius: 200px 100px;
height: 240px;
width: 500px;
margin: 0px;
}
#box1-middle {
background: white;
margin: 0px;
border-width: 0px 15px 0px 15px;
border-color: yellow red green blue;
border-style: solid;
height: 20px;
width: 500px;
}
#box1-bottom {
background: white;
border-width: 0px 15px 15px 15px;
border-color: yellow red green blue;
border-style: solid;
border-bottom-right-radius: 100px 200px;
border-bottom-left-radius: 200px 200px;
height: 240px;
width: 500px;
margin: 0px;
}
</style>
</head>
<body>
<h2>Border Radius - Elliptical</h2>
<div id="box1-top"></div>
<div id="box1-middle"></div>
<div id="box1-bottom"></div><br>
</body>
</html>