mirror of
https://github.com/servo/servo.git
synced 2025-08-10 16:05:43 +01:00
Improve border images
Respect CSS border-image-width. Properly support gradients as a border-image-source. Only emit a border item if the border-width is non-zero for simple borders but still emit one if the item is an image as paint worklet that are not drawn cause servo to hang and fail tests. Add a new test and mark 4 more as passing.
This commit is contained in:
parent
41a2010ee7
commit
fabb70f874
9 changed files with 272 additions and 163 deletions
|
@ -9,11 +9,11 @@
|
|||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
// FIXME(rust-lang/rust#26264): Remove GenericEndingShape and GenericGradientItem.
|
||||
// FIXME(rust-lang/rust#26264): Remove GenericEndingShape, GenericGradientItem,
|
||||
// GenericBackgroundSize and GenericBorderImageSideWidth.
|
||||
|
||||
use app_units::Au;
|
||||
use display_list::ToLayout;
|
||||
use display_list::items::WebRenderImageInfo;
|
||||
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Vector2D};
|
||||
use model::{self, MaybeAuto};
|
||||
use style::computed_values::background_attachment::single_value::T as BackgroundAttachment;
|
||||
|
@ -23,20 +23,22 @@ use style::computed_values::border_image_outset::T as BorderImageOutset;
|
|||
use style::properties::ComputedValues;
|
||||
use style::properties::style_structs::{self, Background};
|
||||
use style::values::Either;
|
||||
use style::values::computed::{Angle, GradientItem, BackgroundSize as ComputedBackgroundSize};
|
||||
use style::values::computed::{Angle, GradientItem, BackgroundSize};
|
||||
use style::values::computed::{BorderImageWidth, BorderImageSideWidth};
|
||||
use style::values::computed::{LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
use style::values::computed::{NumberOrPercentage, Percentage, Position};
|
||||
use style::values::computed::image::{EndingShape, LineDirection};
|
||||
use style::values::generics::NonNegative;
|
||||
use style::values::generics::background::BackgroundSize;
|
||||
use style::values::generics::background::BackgroundSize as GenericBackgroundSize;
|
||||
use style::values::generics::border::{BorderImageSideWidth as GenericBorderImageSideWidth};
|
||||
use style::values::generics::image::{Circle, Ellipse, ShapeExtent};
|
||||
use style::values::generics::image::EndingShape as GenericEndingShape;
|
||||
use style::values::generics::image::GradientItem as GenericGradientItem;
|
||||
use style::values::specified::background::BackgroundRepeatKeyword;
|
||||
use style::values::specified::position::{X, Y};
|
||||
use webrender_api::{BorderDetails, BorderRadius, BorderSide, BorderStyle, ColorF, ExtendMode};
|
||||
use webrender_api::{Gradient, GradientStop, LayoutSize, NinePatchBorder, NinePatchBorderSource};
|
||||
use webrender_api::{NormalBorder, RadialGradient};
|
||||
use webrender_api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF};
|
||||
use webrender_api::{ExtendMode, Gradient, GradientStop, LayoutSize, NinePatchBorder};
|
||||
use webrender_api::{NinePatchBorderSource, NormalBorder, RadialGradient};
|
||||
|
||||
/// A helper data structure for gradients.
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -94,14 +96,14 @@ pub fn get_cyclic<T>(arr: &[T], index: usize) -> &T {
|
|||
/// For a given area and an image compute how big the
|
||||
/// image should be displayed on the background.
|
||||
fn compute_background_image_size(
|
||||
bg_size: ComputedBackgroundSize,
|
||||
bg_size: BackgroundSize,
|
||||
bounds_size: Size2D<Au>,
|
||||
intrinsic_size: Option<Size2D<Au>>,
|
||||
) -> Size2D<Au> {
|
||||
match intrinsic_size {
|
||||
None => match bg_size {
|
||||
BackgroundSize::Cover | BackgroundSize::Contain => bounds_size,
|
||||
BackgroundSize::Explicit { width, height } => Size2D::new(
|
||||
GenericBackgroundSize::Cover | GenericBackgroundSize::Contain => bounds_size,
|
||||
GenericBackgroundSize::Explicit { width, height } => Size2D::new(
|
||||
MaybeAuto::from_style(width.0, bounds_size.width)
|
||||
.specified_or_default(bounds_size.width),
|
||||
MaybeAuto::from_style(height.0, bounds_size.height)
|
||||
|
@ -115,16 +117,20 @@ fn compute_background_image_size(
|
|||
let bounds_aspect_ratio =
|
||||
bounds_size.width.to_f32_px() / bounds_size.height.to_f32_px();
|
||||
match (bg_size, image_aspect_ratio < bounds_aspect_ratio) {
|
||||
(BackgroundSize::Contain, false) | (BackgroundSize::Cover, true) => Size2D::new(
|
||||
bounds_size.width,
|
||||
bounds_size.width.scale_by(image_aspect_ratio.recip()),
|
||||
),
|
||||
(BackgroundSize::Contain, true) | (BackgroundSize::Cover, false) => Size2D::new(
|
||||
bounds_size.height.scale_by(image_aspect_ratio),
|
||||
bounds_size.height,
|
||||
),
|
||||
(GenericBackgroundSize::Contain, false) | (GenericBackgroundSize::Cover, true) => {
|
||||
Size2D::new(
|
||||
bounds_size.width,
|
||||
bounds_size.width.scale_by(image_aspect_ratio.recip()),
|
||||
)
|
||||
},
|
||||
(GenericBackgroundSize::Contain, true) | (GenericBackgroundSize::Cover, false) => {
|
||||
Size2D::new(
|
||||
bounds_size.height.scale_by(image_aspect_ratio),
|
||||
bounds_size.height,
|
||||
)
|
||||
},
|
||||
(
|
||||
BackgroundSize::Explicit {
|
||||
GenericBackgroundSize::Explicit {
|
||||
width,
|
||||
height: NonNegative(LengthOrPercentageOrAuto::Auto),
|
||||
},
|
||||
|
@ -135,7 +141,7 @@ fn compute_background_image_size(
|
|||
Size2D::new(width, width.scale_by(image_aspect_ratio.recip()))
|
||||
},
|
||||
(
|
||||
BackgroundSize::Explicit {
|
||||
GenericBackgroundSize::Explicit {
|
||||
width: NonNegative(LengthOrPercentageOrAuto::Auto),
|
||||
height,
|
||||
},
|
||||
|
@ -145,7 +151,7 @@ fn compute_background_image_size(
|
|||
.specified_or_default(own_size.height);
|
||||
Size2D::new(height.scale_by(image_aspect_ratio), height)
|
||||
},
|
||||
(BackgroundSize::Explicit { width, height }, _) => Size2D::new(
|
||||
(GenericBackgroundSize::Explicit { width, height }, _) => Size2D::new(
|
||||
MaybeAuto::from_style(width.0, bounds_size.width)
|
||||
.specified_or_default(own_size.width),
|
||||
MaybeAuto::from_style(height.0, bounds_size.height)
|
||||
|
@ -795,32 +801,60 @@ pub fn calculate_inner_border_radii(
|
|||
radii
|
||||
}
|
||||
|
||||
/// Given an image and a border style constructs a border image.
|
||||
/// Create the image border details.
|
||||
///
|
||||
/// See: https://drafts.csswg.org/css-backgrounds-3/#border-images
|
||||
pub fn build_image_border_details(
|
||||
webrender_image: WebRenderImageInfo,
|
||||
/// For a fragment with a given with a given "bounds" that has a "border_width"
|
||||
/// for all four sides calculate the outset for the image border. Call the
|
||||
/// "build_source" function to get the image which is either a gradient
|
||||
/// or an image. The supplied "fallback_size" is used to size gradients
|
||||
/// and other images without an intrinsic size. The actual size of the image
|
||||
/// is also returned. Apply all other attributes for border images and return
|
||||
/// the "NinePatchBorder".
|
||||
pub fn build_border_image_details<F>(
|
||||
bounds: Rect<Au>,
|
||||
border_width: SideOffsets2D<Au>,
|
||||
border_style_struct: &style_structs::Border,
|
||||
outset: SideOffsets2D<f32>,
|
||||
) -> Option<BorderDetails> {
|
||||
let corners = &border_style_struct.border_image_slice.offsets;
|
||||
build_source: F,
|
||||
) -> Option<(NinePatchBorder, BorderWidths)>
|
||||
where
|
||||
F: FnOnce(Size2D<Au>) -> Option<(NinePatchBorderSource, Size2D<u32>)>,
|
||||
{
|
||||
let border_image_outset =
|
||||
calculate_border_image_outset(border_style_struct.border_image_outset, border_width);
|
||||
let border_image_area = bounds.outer_rect(border_image_outset).size;
|
||||
let border_image_width = calculate_border_image_width(
|
||||
&border_style_struct.border_image_width,
|
||||
border_width.to_layout(),
|
||||
border_image_area,
|
||||
);
|
||||
let border_image_repeat = &border_style_struct.border_image_repeat;
|
||||
if let Some(image_key) = webrender_image.key {
|
||||
Some(BorderDetails::NinePatch(NinePatchBorder {
|
||||
source: NinePatchBorderSource::Image(image_key),
|
||||
width: webrender_image.width,
|
||||
height: webrender_image.height,
|
||||
slice: SideOffsets2D::new(
|
||||
corners.0.resolve(webrender_image.height),
|
||||
corners.1.resolve(webrender_image.width),
|
||||
corners.2.resolve(webrender_image.height),
|
||||
corners.3.resolve(webrender_image.width),
|
||||
),
|
||||
fill: border_style_struct.border_image_slice.fill,
|
||||
repeat_horizontal: border_image_repeat.0.to_layout(),
|
||||
repeat_vertical: border_image_repeat.1.to_layout(),
|
||||
outset: outset,
|
||||
}))
|
||||
let border_image_fill = border_style_struct.border_image_slice.fill;
|
||||
let border_image_slice = &border_style_struct.border_image_slice.offsets;
|
||||
|
||||
if let Some((source, size)) = build_source(border_image_area) {
|
||||
Some((
|
||||
NinePatchBorder {
|
||||
source: source,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
slice: SideOffsets2D::new(
|
||||
border_image_slice.0.resolve(size.height),
|
||||
border_image_slice.1.resolve(size.width),
|
||||
border_image_slice.2.resolve(size.height),
|
||||
border_image_slice.3.resolve(size.width),
|
||||
),
|
||||
fill: border_image_fill,
|
||||
repeat_horizontal: border_image_repeat.0.to_layout(),
|
||||
repeat_vertical: border_image_repeat.1.to_layout(),
|
||||
outset: SideOffsets2D::new(
|
||||
border_image_outset.top.to_f32_px(),
|
||||
border_image_outset.right.to_f32_px(),
|
||||
border_image_outset.bottom.to_f32_px(),
|
||||
border_image_outset.left.to_f32_px(),
|
||||
),
|
||||
},
|
||||
border_image_width,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -833,7 +867,7 @@ fn calculate_border_image_outset_side(outset: LengthOrNumber, border_width: Au)
|
|||
}
|
||||
}
|
||||
|
||||
pub fn calculate_border_image_outset(
|
||||
fn calculate_border_image_outset(
|
||||
outset: BorderImageOutset,
|
||||
border: SideOffsets2D<Au>,
|
||||
) -> SideOffsets2D<Au> {
|
||||
|
@ -844,3 +878,28 @@ pub fn calculate_border_image_outset(
|
|||
calculate_border_image_outset_side(outset.3, border.left),
|
||||
)
|
||||
}
|
||||
|
||||
fn calculate_border_image_width_side(
|
||||
border_image_width: BorderImageSideWidth,
|
||||
border_width: f32,
|
||||
total_length: Au,
|
||||
) -> f32 {
|
||||
match border_image_width {
|
||||
GenericBorderImageSideWidth::Length(v) => v.to_used_value(total_length).to_f32_px(),
|
||||
GenericBorderImageSideWidth::Number(x) => border_width * x,
|
||||
GenericBorderImageSideWidth::Auto => border_width,
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_border_image_width(
|
||||
width: &BorderImageWidth,
|
||||
border: BorderWidths,
|
||||
border_area: Size2D<Au>,
|
||||
) -> BorderWidths {
|
||||
BorderWidths {
|
||||
left: calculate_border_image_width_side(width.3, border.left, border_area.width),
|
||||
top: calculate_border_image_width_side(width.0, border.top, border_area.height),
|
||||
right: calculate_border_image_width_side(width.1, border.right, border_area.width),
|
||||
bottom: calculate_border_image_width_side(width.2, border.bottom, border_area.height),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue