use app_units::Au;
use euclid::default::{Rect, SideOffsets2D as UntypedSideOffsets2D, Size2D as UntypedSize2D};
use euclid::{SideOffsets2D, Size2D};
use style::computed_values::border_image_outset::T as BorderImageOutset;
use style::properties::style_structs::Border;
use style::values::computed::{
BorderCornerRadius, BorderImageSideWidth, BorderImageWidth, NonNegativeLengthOrNumber,
NumberOrPercentage,
};
use style::values::generics::rect::Rect as StyleRect;
use style::values::generics::NonNegative;
use webrender_api::units::{LayoutSideOffsets, LayoutSize};
use webrender_api::{BorderRadius, BorderSide, BorderStyle, ColorF, NormalBorder};
use crate::display_list::ToLayout;
fn corner_radius(
radius: &BorderCornerRadius,
containing_size: UntypedSize2D<Au>,
) -> UntypedSize2D<Au> {
let w = radius.0.width().to_used_value(containing_size.width);
let h = radius.0.height().to_used_value(containing_size.height);
Size2D::new(w, h)
}
fn scaled_radii(radii: BorderRadius, factor: f32) -> BorderRadius {
BorderRadius {
top_left: radii.top_left * factor,
top_right: radii.top_right * factor,
bottom_left: radii.bottom_left * factor,
bottom_right: radii.bottom_right * factor,
}
}
fn overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius {
fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 {
let required = radius_a + radius_b;
if required <= edge_length {
1.0
} else {
edge_length / required
}
}
let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, 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.height, radii.bottom_left.height, 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);
if min_factor < 1.0 {
scaled_radii(radii, min_factor)
} else {
radii
}
}
pub fn radii(abs_bounds: Rect<Au>, border_style: &Border) -> BorderRadius {
overlapping_radii(
abs_bounds.size.to_layout(),
BorderRadius {
top_left: corner_radius(&border_style.border_top_left_radius, abs_bounds.size)
.to_layout(),
top_right: corner_radius(&border_style.border_top_right_radius, abs_bounds.size)
.to_layout(),
bottom_right: corner_radius(&border_style.border_bottom_right_radius, abs_bounds.size)
.to_layout(),
bottom_left: corner_radius(&border_style.border_bottom_left_radius, abs_bounds.size)
.to_layout(),
},
)
}
pub fn inner_radii(mut radii: BorderRadius, offsets: UntypedSideOffsets2D<Au>) -> BorderRadius {
fn inner_length(x: f32, offset: Au) -> f32 {
0.0_f32.max(x - offset.to_f32_px())
}
radii.top_left.width = inner_length(radii.top_left.width, offsets.left);
radii.bottom_left.width = inner_length(radii.bottom_left.width, offsets.left);
radii.top_right.width = inner_length(radii.top_right.width, offsets.right);
radii.bottom_right.width = inner_length(radii.bottom_right.width, offsets.right);
radii.top_left.height = inner_length(radii.top_left.height, offsets.top);
radii.top_right.height = inner_length(radii.top_right.height, offsets.top);
radii.bottom_left.height = inner_length(radii.bottom_left.height, offsets.bottom);
radii.bottom_right.height = inner_length(radii.bottom_right.height, offsets.bottom);
radii
}
pub fn simple(color: ColorF, style: BorderStyle) -> NormalBorder {
let side = BorderSide { color, style };
NormalBorder {
left: side,
right: side,
top: side,
bottom: side,
radius: BorderRadius::zero(),
do_aa: true,
}
}
fn side_image_outset(outset: NonNegativeLengthOrNumber, border_width: Au) -> Au {
match outset {
NonNegativeLengthOrNumber::Length(length) => length.into(),
NonNegativeLengthOrNumber::Number(factor) => border_width.scale_by(factor.0),
}
}
pub fn image_outset(
outset: BorderImageOutset,
border: UntypedSideOffsets2D<Au>,
) -> UntypedSideOffsets2D<Au> {
SideOffsets2D::new(
side_image_outset(outset.0, border.top),
side_image_outset(outset.1, border.right),
side_image_outset(outset.2, border.bottom),
side_image_outset(outset.3, border.left),
)
}
fn side_image_width(
border_image_width: &BorderImageSideWidth,
border_width: f32,
total_length: Au,
) -> f32 {
match border_image_width {
BorderImageSideWidth::LengthPercentage(v) => v.to_used_value(total_length).to_f32_px(),
BorderImageSideWidth::Number(x) => border_width * x.0,
BorderImageSideWidth::Auto => border_width,
}
}
pub fn image_width(
width: &BorderImageWidth,
border: LayoutSideOffsets,
border_area: UntypedSize2D<Au>,
) -> LayoutSideOffsets {
LayoutSideOffsets::new(
side_image_width(&width.0, border.top, border_area.height),
side_image_width(&width.1, border.right, border_area.width),
side_image_width(&width.2, border.bottom, border_area.height),
side_image_width(&width.3, border.left, border_area.width),
)
}
fn resolve_percentage(value: NonNegative<NumberOrPercentage>, length: i32) -> i32 {
match value.0 {
NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as i32,
NumberOrPercentage::Number(n) => n.round() as i32,
}
}
pub fn image_slice<U>(
border_image_slice: &StyleRect<NonNegative<NumberOrPercentage>>,
size: Size2D<i32, U>,
) -> SideOffsets2D<i32, U> {
SideOffsets2D::new(
resolve_percentage(border_image_slice.0, size.height),
resolve_percentage(border_image_slice.1, size.width),
resolve_percentage(border_image_slice.2, size.height),
resolve_percentage(border_image_slice.3, size.width),
)
}